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

本文关键字:无限循环 这里 为什么 线程 问题 | 更新日期: 2023-09-27 17:57:49

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({计数器++;}我没有无限循环,但如果锁像示例中那样,它会运行无限循环

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

可能是当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 ...

您会注意到,一旦Counter变为20,t2就会停止,但t1不会注意到这一点。它已经进入循环(或决定进入循环(,认为Counter是1(或可能是2或其他什么——只是不是20(。

这里的问题是您的线路:

while (Counter != 20)

由于您锁定了计数器的增量,在某个时刻,Counter可以等于19。两个线程都可以进行检查,然后在内部递增计数器,使其在线程再次检查之前为21。

也就是说,即使两个线程没有同时达到20,一个线程也可能看到20并停止,而另一个线程在达到时的值为21,循环将永远继续。

顺便说一句,你的"修复"(只锁定增量(并不能真正修复它——它只会降低出错的可能性。原因是Console.WriteLine调用要慢得多,所以更多的处理时间发生在锁中,这使得线程更有可能在再次看到条件检查之前通过条件检查。然而,只要锁定计数器增量,这种情况仍然可能发生(尽管这种情况更为罕见。(

你可以通过一个更灵活的条件来很容易地纠正这一点,比如:

 while (Counter < 20)

这将导致线程在达到20或更高时立即退出。

根据代码的编写方式,两个线程都可以在计算各自的while子句之前递增Counter。在这种情况下,Counter可以在命中下一个while之前从19变为21

试着把你的循环重构成这样的东西:

while (true) {
    lock (_lock) {
        Counter++;
        Console.WriteLine("Thread {0} : the value of the counter is {1}",
            name.ToString(), Counter);
        if (Counter >= 20) {
            break;
        }
    }
}