Блокировка объектов не работает для безопасности потоков - PullRequest
1 голос
/ 09 декабря 2010

Я тестировал потокобезопасность для лучшего понимания, и вот что я сделал:

У меня есть тип с именем ThreadSample, который имеет два метода, и здесь происходит блокировка:

internal class ThreadTime
    {

        public void doSomething(string message)
        {
            lock (this)
            {
                DialogResult t = MessageBox.Show(message);
                Thread.Sleep(2000);
            }
        }

        public void anotherLife(string message)
        {
            MessageBox.Show("This is coming from anotherLife method and and current threadNumber is " + message);
        }
    }

По сути, идея заключается в том, что при вызове doSomething() он должен блокировать все объекты, и другие потоки могут даже вызывать метод anotherLife, поскольку они ожидают, пока другие потоки снимают блокировку.

Это логика для имитации снятия блокировки:

public partial class Form1 : Form
{
    private ThreadTime time;
    private Thread thread;

    public Form1()
    {
        InitializeComponent();
    }

    private void Form1_Load(object sender, EventArgs e)
    {
        thread = new Thread(new ThreadStart(workerThread));
        time = new ThreadTime();
    }

    private void button1_Click(object sender, EventArgs e)
    {
        thread.Start();
        //Thread.Sleep(1000);
        time.anotherLife("Current thread is = " + "UI Thread");  
    }

    private void workerThread()
    {
        //time.doSomething("Current thread is = " + Thread.CurrentThread.ManagedThreadId);
        time.doSomething("Worker Thread");
    }
}

Как вы можете видеть в коде справа:

При инициализации Form создаются новые Thread и ThreadSample. Затем, когда пользователь нажимает на button1, запускается поток, и UIThread достигает и вызывает anotherLife, который сначала не является поточно-ориентированным.

В любом случае, вывод:

  • Одновременно отображаются два MessageBox.

Я ожидал, что когда новый поток вызовет doSomething(), он получит блокировку объекта и UIThread ожидает, пока блокировка будет снята, чтобы вызвать метод anotherLife.

Может кто-нибудь объяснить, почему?

Спасибо.

Ответы [ 3 ]

5 голосов
/ 09 декабря 2010

Я ожидал, что когда новый поток вызовет doSomething (), он получит блокировку объекта, а UIThread ожидает освобождения блокировки, чтобы иметь возможность вызвать метод anotherLife.

UIThread не будет ждать разблокировки блокировки, прежде чем разрешить anotherLife продолжить, потому что anotherLife не выполняет блокировку. Оба потока должны выполнить оператор lock (блокирующий один и тот же объект), чтобы получить поведение, которое вы ищете. Попробуйте изменить его на что-то вроде:

public void anotherLife(string message)
{
    lock (this) 
    {
        MessageBox.Show("This is coming from anotherLife method and and current threadNumber is " + message);
    }
}
1 голос
/ 09 декабря 2010

Только ваша нить наблюдает за блокировкой

Вам нужно изменить

private void button1_Click(object sender, EventArgs e)
    {
        thread.Start();
        //Thread.Sleep(1000);
        time.anotherLife("Current thread is = " + "UI Thread");  
    }

до

private void button1_Click(object sender, EventArgs e)
    {
        thread.Start();
        //Thread.Sleep(1000);
lock(time)
{
        time.anotherLife("Current thread is = " + "UI Thread");  
}
    }

Исходя из вашего кода, кажется, вы думаете, что установка блокировки на объект означает, что объект не может быть доступен для чего-либо еще. Это не относится к делу. Блокировка на объекте просто означает, что другая блокировка не может быть наложена на объект, пока не будет снята первая блокировка.

Вы получаете доступ к объекту из двух мест в вашем коде, одно в потоке, а другое в событии кнопки. Вам нужен замок в обоих местах.

1 голос
/ 09 декабря 2010

Ну, lock(this) или lock(someThing) могут быть немного вводящей в заблуждение метафорой.

Ничего не делается 'к' this, вместо этого аргумент к lock используется в качестве токена. Все потоки, обращающиеся к определенному ресурсу, должны использовать один и тот же токен (объект) для запроса доступа, в противном случае ваш код поврежден.

Вот почему часто используется вспомогательный объект:

private List<string> myList = ...;
private object myLock = new object();

lock(myLock)
{
   myList.Add("foo");
}

Эта схема работает, только если все потоки заблокированы на myLock до изменения myList.
Это считается «лучшей практикой», потому что не гарантируется, что List <> безопасен для блокировки.

...