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();
无论lock{...}
块是否存在,有时它会打印'99'而有时'100'。我的代码有什么问题吗?
答案 0 :(得分:4)
这里的问题是你正在开始攻击线程,并且在调用输出到控制台的时候它们并没有完全完成。
循环从 i 线程开始,我们可以想象这些蜜蜂作为工作收集,而不是所有人都去相同的食物来源,所以有些人需要更长的时间来回归;然后,回到蜂巢,我们突然说:“嘿,蜜蜂,我需要一个人数!”,“...... 1,2,3 ......,只有三个?”不,有些 i-3 仍在外面徘徊!
所以,我们的想法是,我们必须有一个工作完成时间的指示,或者一个归类信号,以便让所有蜜蜂回到蜂巢的人数。这可以通过Join
或手动状态检查来完成(基本上你可以坚持到最后一个旅行者返回。)
答案 1 :(得分:3)
你不等待线程完成。在输出计数之前,有多少人完成了他们的工作,这完全是运气问题。
添加
for (int i = 0; i < 100; i++)
{
threadArray[i].Join();
}
在WriteLine
之前,您总是得到100
。
答案 2 :(得分:1)
你应该用锁来保护你的getter public int Count { get { return m_count; } }
,否则一个线程B可能正在读取该值,而另一个线程A正在你的Add方法中更新count,这可能会导致你得到一个不一致的视图你的数据。
答案 3 :(得分:1)
这是一个经典的race condition,我觉得你很幸运在调用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);
考虑使用CountdownEvent或类似的方法进行一些同步。
答案 4 :(得分:1)
对Count
的调用发生时,不确定。它可能发生在任何线程完成之前,或者在所有线程完成之后,甚至在中间的某个地方。如果你想等到所有线程都完成,你应该加入它们。
答案 5 :(得分:0)
不确定这是您正在寻找的答案,但要以线程安全的方式递增计数器,您应该使用Interlocked.Increment
docs
答案 6 :(得分:0)
打印计数时,您不知道线程是否已完成。代码中的“正确”输出可以是0到100之间的任何数字。
要确保获得100作为输出,在所有线程完成之前,您不能获得Count
的值。您可以通过在所有线程上调用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;