对象锁定不适用于Thread Safety

时间:2010-12-09 17:52:22

标签: c# thread-safety

我正在测试线程安全性以便更好地掌握,这就是我所做的:

我有一个名为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时,会创建新的ThreadThreadSample。然后当用户点击button1时,线程就会启动,UIThread正在到达并调用anotherLife,这在一开始就不是线程安全的。

无论如何,输出是:

  • 同时显示两个MessageBox。

我期待的是当新线程调用doSomething()时,它获取对象的锁定并且UIThread等待释放锁以便能够调用anotherLife方法

有人可以解释原因吗?

感谢。

3 个答案:

答案 0 :(得分:5)

  

我期待的是当新线程调用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 :(得分:1)

嗯,lock(this)lock(someThing)可能是一个误导性的比喻。

'this没有做任何事情,而是将lock的参数用作代币。访问特定资源的所有线程必须使用相同的令牌(对象)来请求访问,否则您的代码就会被破坏。

这就是为什么经常使用辅助对象的原因:

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

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

此方案仅在所有线程在更改myLock之前锁定myList时才有效 它被认为是“最佳实践”,因为不能保证List&lt;&gt;可以安全锁定。

答案 2 :(得分:1)

只有您的线程会观察锁

您需要更改

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");  
}
    }

根据您的代码,您似乎认为对某个对象进行锁定意味着该对象无法被其他任何内容访问。事实并非如此。锁定对象只意味着在释放第一个锁之前,另一个锁可能不会放在对象上。

您可以从代码中的两个位置访问该对象,一个位于线程中,另一个位于按钮事件中。你需要锁定这两个地方。