C#线程问题为什么无限循环在这里?

时间:2011-05-26 15:27:22

标签: c# multithreading

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)                     {                         反++;                      }          我没有无限循环,但如果锁是示例,则运行无限循环

3 个答案:

答案 0 :(得分:9)

可能是当Counter到达19时,两个线程都进入循环,并且在再次测试该值之前最终会增加到21。

您需要在阅读Counter的值时按住锁定。对Counter进行仔细检查可能就足够了(在保持锁定的同时在while循环内重新读取它)。但是,我不确定这一点,因为我的头脑无法跟踪本机,.NET,Java等各种线程内存模型的所有细节。即使在.NET上,ECMA模型显然也不同于MS对其CLR的保证(参见http://msdn.microsoft.com/en-us/magazine/cc163715.aspxhttp://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 ...

您会注意到t2Counter到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;
        }
    }
}