简体   繁体   English

为什么“锁定”块不起作用?

[英]Why “lock” block not work?

public class ThreadInteroperateWithLock
{
    private int m_count;
    private object m_synLock;
    public ThreadInteroperateWithLock() 
    {
        m_count = 0;
        m_synLock = new object();
    }

    public int Count { get { return m_count; } }

    public void Add() 
    {
        //just simulate some work
        int temp=0;
        for (int i = 0; i < 10000; i++)
        {
            temp++;
        }

        //really job
        lock (m_synLock)
        {
            m_count++;
        }
    }
}

This code is in a console application: 此代码位于控制台应用程序中:

ThreadInteroperateWithLock ope = new ThreadInteroperateWithLock();
Thread[] threadArray = new Thread[100];
for (int i = 0; i < 100; i++)
{
    Thread thread = new Thread(new ThreadStart(ope.Add));
    thread.IsBackground = false;
    threadArray[i] = thread;
}
for (int i = 0; i < 100; i++)
{
    threadArray[i].Start();
}
Console.WriteLine(ope.Count);
Console.ReadKey();

Sometime it prints '99' and sometime '100', regardless of whether the lock{...} block exists or not. 无论lock{...}块是否存在,有时它会打印'99'而有时'100'。 Is there any wrong in my code? 我的代码有什么问题吗?

The problem here would be that you're kicking off the threads and they're not entirely finished by the time of your call to write output to the console. 这里的问题是你正在开始攻击线程,并且在调用输出到控制台的时候它们并没有完全完成。

The loop starts off i threads, we'll imagine these as bees going of collecting as work, not all going to the same food source so some take longer to return than others; 循环开始我的线程,我们会想象这些蜜蜂作为工作收集,而不是所有去往相同的食物来源,所以有些人需要更长的时间返回; then, back at the hive, we suddenly say: "Hey, bees, I need a headcount!", "...1, 2, 3..., only three?" 然后,回到蜂巢,我们突然说:“嘿,蜜蜂,我需要一个人数!”,“...... 1,2,3 ......,只有三个?” No, some i-3 are still scrounging around outside! 不,有些i-3仍然在外面徘徊!

So, the idea is that we must have an indicator of when work is completed, or a homing signal of sorts to get all bees back to the hive for the headcount. 因此,我们的想法是,我们必须有一个工作完成时间的指示,或者各种归巢信号,以便让所有蜜蜂回到蜂巢的人数。 This can be done with Join , or manual state checking (which essentially has you holding out until the last sojourner returns.) 这可以通过Join或手动状态检查来完成(基本上你可以坚持到最后一个旅行者返回。)

You're not waiting for the threads to finish. 你不是在等待线程完成。 It'e entirely a matter of luck how many have done their work before you output the count. 在输出计数之前,有多少人完成了他们的工作,这完全是一个幸运的问题。

Add

        for (int i = 0; i < 100; i++)
        {
            threadArray[i].Join();

        }

before the WriteLine , and you always get 100 . WriteLine之前,你总是得到100

You should protect your getter public int Count { get { return m_count; } } 你应该保护你的getter public int Count { get { return m_count; } } public int Count { get { return m_count; } } with a lock to, else a thread B could be reading that value, while another thread A is updating count in your Add method, this can cause you to get an inconsistent view of your data. public int Count { get { return m_count; } }配锁,否则线程B可以被读取值,而另一个线程A在您的Add方法更新计数,这可以使你得到你的数据不一致的看法。

It's a classic race condition , and I consider you lucky to get anywhere near 100 by the time your Console.WriteLine is invoked. 这是一个经典的竞争条件 ,我认为在调用Console.WriteLine时,你可以幸运地接近100。

for (int i = 0; i < 100; i++)
{
    threadArray[i].Start();
}
//absolutely no guarantee that **ANY** threads have completed before next line
Console.WriteLine(ope.Count);

Consider using a CountdownEvent or similar to do a bit of synchronization. 考虑使用CountdownEvent或类似方法进行一些同步。

It is not deterministic when your call to Count happens. 你对Count的调用发生时,这不是确定性的。 It may happen before any thread has finished, or after all threads have finished or even somewhere in the middle. 它可能发生在任何线程完成之前,或者在所有线程完成之后,甚至在中间的某个地方。 If you want to wait until all thread have finished, you should join them. 如果你想等到所有线程都完成,你应该加入它们。

不确定这是你正在寻找的答案,但要以线程安全的方式增加计数器,你应该使用Interlocked.Increment docs

When you print the count you have no idea of whether or not the threads have completed or not. 打印计数时,您不知道线程是否已完成。 A "correct" output in your code could be any number between 0 and 100. 代码中的“正确”输出可以是0到100之间的任何数字。

To be certain to get 100 as output, you mustn't get the value of Count until all threads have completed. 要确保获得100作为输出,在完成所有线程之前,不得获得Count的值。 You can do that by calling Thread.Join on all threads or (even better) use something like Parallel.For to start the threads: 你可以通过在所有线程上调用Thread.Join或者甚至更好地使用Parallel.For来启动线程:

Parallel.For(0, 100, idx => ope.Add);
// When we reach this line, we know all threads have completed so we can 
// safely get the count
var count = ope.Count;

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM