为什么要“锁定”阻止不起作用?
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++;
}
}
}
此代码位于控制台应用程序中:
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();
有时会打印“99”,有时会打印“100”,无论 lock{...}
块是否存在。我的代码有什么错误吗?
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. Is there any wrong in my code?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(7)
这里的问题是,您正在启动线程,并且在调用将输出写入控制台时它们尚未完全完成。
循环从i个线程开始,我们将它们想象为蜜蜂将收集作为工作,并不是所有的都去同一个食物源,所以有些比其他的需要更长的时间返回;然后,回到蜂巢,我们突然说:“嘿,蜜蜂,我需要统计人数!”,“...1、2、3...,只有三个?”不,一些i-3仍在外面搜寻!
因此,我们的想法是,我们必须有一个工作何时完成的指示器,或者某种归巢信号,让所有蜜蜂返回蜂巢进行人数统计。这可以通过
加入
来完成,或手动状态检查(这本质上是让你坚持到最后一个旅居者返回。)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?" No, some i-3 are still scrounging around outside!
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.)您不是在等待线程完成。在输出计数之前有多少人完成了工作完全取决于运气。
添加,您始终会得到
100
。在
WriteLine
之前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
before the
WriteLine
, and you always get100
.你应该保护你的 getter
public int Count { get { return m_count; } }
带有锁,否则线程 B 可能正在读取该值,而另一个线程 A 正在 Add 方法中更新计数,这可能会导致您获得不一致的数据视图。You should protect your getter
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.当您调用
Count
时,它不是确定性的。它可能发生在任何线程完成之前,或者所有线程完成之后,甚至在中间的某个地方。如果你想等到所有线程都完成,你应该加入它们。It is not deterministic when your call to
Count
happens. 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.这是一个经典的竞赛条件,我认为你到时候能达到接近 100 就很幸运了您的
Console.WriteLine
被调用。考虑使用 CountdownEvent 或类似于做一些同步。
It's a classic race condition, and I consider you lucky to get anywhere near 100 by the time your
Console.WriteLine
is invoked.Consider using a CountdownEvent or similar to do a bit of synchronization.
不确定这是否是您正在寻找的答案,但要以线程安全的方式增加计数器,您应该使用
Interlocked.Increment
文档Not sure that this is the answer you're looking for, but to incrememnt a counter in a threadsafe way you should use
Interlocked.Increment
docs当您打印计数时,您不知道线程是否已完成。代码中的“正确”输出可以是 0 到 100 之间的任意数字。
要确保获得 100 作为输出,在所有线程完成之前不得获取
Count
的值。您可以通过调用 Thread.Join 来完成此操作在所有线程上或(甚至更好)使用类似 Parallel.For 启动线程: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.
To be certain to get 100 as output, you mustn't get the value of
Count
until all threads have completed. You can do that by calling Thread.Join on all threads or (even better) use something like Parallel.For to start the threads: