using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
namespace ThreadExample
{
public class Info
{
public int Counter;
private static object _lock = new object();
private List<Thread> ThreadList;
public Info(int counter)
{
Counter = counter;
ThreadList = new List<Thread>();
ThreadList.Add(new Thread(ThreadBody));
ThreadList.Add(new Thread(ThreadBody));
ThreadList[0].Name = "t1";
ThreadList[1].Name = "t2";
}
public void Start()
{
ThreadList.ForEach(t => t.Start(t.Name));
}
public void ThreadBody(object name)
{
while (Counter != 20)
{
lock (_lock)
{
Counter++;
Console.WriteLine("Thread {0} : the value of the counter is {1}", name.ToString(), Counter);
}
}
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ThreadExample
{
class Program
{
static void Main(string[] args)
{
Info info = new Info(0);
info.Start();
}
}
}
如果锁只是锁定计数器++ 锁(_lock) { 反++; } 我没有无限循环,但如果锁是示例,则运行无限循环
答案 0 :(得分:9)
可能是当Counter
到达19时,两个线程都进入循环,并且在再次测试该值之前最终会增加到21。
您需要在阅读Counter
的值时按住锁定。对Counter
进行仔细检查可能就足够了(在保持锁定的同时在while
循环内重新读取它)。但是,我不确定这一点,因为我的头脑无法跟踪本机,.NET,Java等各种线程内存模型的所有细节。即使在.NET上,ECMA模型显然也不同于MS对其CLR的保证(参见http://msdn.microsoft.com/en-us/magazine/cc163715.aspx和http://www.bluebytesoftware.com/blog/PermaLink,guid,543d89ad-8d57-4a51-b7c9-a821e3992bf6.aspx)。有关为什么重复检查可能会或可能不起作用的详细信息,请搜索“双重检查锁定” - 显然应该很简单的事情背后有很多复杂性。
例如,这是我机器上运行的片段:
Thread t1 : the value of the counter is 1
Thread t2 : the value of the counter is 2
Thread t2 : the value of the counter is 3
Thread t2 : the value of the counter is 4
Thread t2 : the value of the counter is 5
Thread t2 : the value of the counter is 6
Thread t2 : the value of the counter is 7
Thread t2 : the value of the counter is 8
Thread t2 : the value of the counter is 9
Thread t2 : the value of the counter is 10
Thread t2 : the value of the counter is 11
Thread t2 : the value of the counter is 12
Thread t2 : the value of the counter is 13
Thread t2 : the value of the counter is 14
Thread t2 : the value of the counter is 15
Thread t2 : the value of the counter is 16
Thread t2 : the value of the counter is 17
Thread t2 : the value of the counter is 18
Thread t2 : the value of the counter is 19
Thread t2 : the value of the counter is 20
Thread t1 : the value of the counter is 21
Thread t1 : the value of the counter is 22
... Thread t1 never stops ...
您会注意到t2
在Counter
到20后停止,但t1
没有注意到。它已经进入循环(或者决定进入循环),认为Counter
是1(或者可能是2或其他东西 - 只是不是20)。
答案 1 :(得分:2)
问题在于你的界限:
while (Counter != 20)
由于你要锁定增量到计数器,在某些时候,Counter
可以等于19.两个线程都可以进行检查,然后在内部递增计数器,在线程再次检查之前使其为21。
话虽如此,即使两个线程没有同时命中,一个线程可能会看到20并停止,而另一个线程在命中时值为21,并且循环将继续永远。
你的“修复”(仅锁定增量)并没有真正解决它,顺便说一句 - 它只是使错误案例不太可能发生。这样做的原因是Console.WriteLine
调用要慢得多,因此锁中会发生更多的处理时间,这使得线程在再次看到它之前更有可能超过条件检查。但是,只有锁定计数器增量仍然会发生这种情况(尽管它更为罕见。)
您可以通过更灵活的条件轻松纠正此问题,例如:
while (Counter < 20)
这会导致线程在达到20 或更高时立即退出。
答案 2 :(得分:1)
编写代码的方式,两个线程可以在各自的Counter
子句被评估之前递增while
。在这种情况下,Counter
可以在下一个19
被点击之前从21
转到while
。
尝试将循环重构为:
while (true) {
lock (_lock) {
Counter++;
Console.WriteLine("Thread {0} : the value of the counter is {1}",
name.ToString(), Counter);
if (Counter >= 20) {
break;
}
}
}