[英]Race condition in program
我是新手。 因此,我只是一个接一个地创建了4个专用线程(而不是使用Tasks),并为它们提供了一些有用的功能。 但是,这使我对startProcessing函数中的竞争条件产生了疑问,在这里我只是将currentIndex上的值存储到锁内的局部变量中。 但是,我跑了很多次,但不能满足没有种族问题或其他任何问题。
请帮我清除这个东西。
class Program
{
private static object syncLock = new object();
int num = 0;
int currentIndex = 0;
static void Main(string[] args)
{
Program p = new Program();
p.num = 1000;
p.callThreadCreation();
while (p.num > 0)
{
Thread.Sleep(1000);
}
Console.WriteLine("All done");
}
public void callThreadCreation()
{
for (int i = 1; i <= 4; i++)
{
string name = "T" + i;
Thread T = new Thread(new ThreadStart(() => startProcessing()));
T.Name = name;
T.Start();
}
}
**private void startProcessing()
{
while (num > 0)
{
int tempIndex;
lock (syncLock)
{
tempIndex = currentIndex;
}
Interlocked.Decrement(ref num);
Interlocked.Increment(ref currentIndex);
print(tempIndex);
Thread.Sleep(1000);
}
}**
private void print(int x)
{
Console.WriteLine(x);
}
}
考虑startProcessing
方法。 想象一下,所有四个线程while
到达while
检查。 由于循环检查不同步,因此当num
为2时,所有四个循环都可能进入循环。
while
检查。 看到num>0。进入循环。 释放上下文。 while
检查。 Num仍为2,因此> 0。 while
检查的保护。 Decrement操作将完全是原子的并且是同步的,但是当这四个操作完成时,您仍将处于-2。 您有> 0的测试,但是它是在锁定之外执行的。 因此,您的currentIndex
可能会增加1000倍以上,我认为可能会不必要地进入循环的每个额外线程增加1003倍。 我已经稍微修改了您的原始程序(在Thread.Sleep
和Console.WriteLine()
周围移动,以使竞争条件更易于检测。此外,我将最高计数更改为11,而不是4的倍数(该数字从理论上讲,这根本不会改变行为,而只是改变发生的可能性 。
class Program
{
private static object syncLock = new object();
int num = 0;
// make static so we can read in Main()
static int currentIndex = 0;
static void Main(string[] args)
{
Program p = new Program();
p.num = 11;
p.callThreadCreation();
while (p.num > 0)
{
Thread.Sleep(1000);
}
Console.WriteLine("All done {0}", currentIndex);
Console.Read();
}
public void callThreadCreation()
{
for (int i = 1; i <= 4; i++)
{
string name = "T" + i;
Thread T = new Thread(new ThreadStart(() => startProcessing()));
T.Name = name;
T.Start();
}
}
private void startProcessing()
{
while (num > 0)
{
int tempIndex;
lock (syncLock)
{
tempIndex = currentIndex;
}
// putting this in front of the increment/decrement operations makes the race condition more likely
print(tempIndex);
Interlocked.Decrement(ref num);
Interlocked.Increment(ref currentIndex);
Thread.Sleep(1000);
}
}
private void print(int x)
{
Console.WriteLine(x);
}
}
如果尝试此程序,最终应该在最后看到程序输出12。 下面是我想出的解决比赛条件的程序。 我承认这不是最优雅的,但是在这样一个简单的示例中,它可能不需要那么优雅。
class Program
{
private readonly object syncLock = new object();
private int num = 0;
private static int currentIndex = 0;
private static void Main(string[] args)
{
Program p = new Program();
p.num = 11;
// just return the threads, so we can join
var threadList = p.callThreadCreation();
// don't need Thread.Sleep() here
//while (p.num > 0)
//{
// Thread.Sleep(1000);
//}
foreach (var t in threadList) { t.Join(); }
Console.WriteLine("All done {0}", currentIndex);
Console.Read();
}
public IReadOnlyList<Thread> callThreadCreation()
{
var threadList = new List<Thread>();
for (int i = 1; i <= 4; i++)
{
// this is easier to read for me
var T = new Thread(startProcessing)
{
Name = "T" + i
};
// just return the threads, so we can join
threadList.Add(T);
T.Start();
}
// just return the threads, so we can join
return threadList;
}
private void startProcessing()
{
while (true)
{
int tempIndex;
lock (syncLock)
{
// I'm not sure what the point of this line is... sometimes "tempIndex" will
// be the same between two threads, but this just seems like a progress report
// for the person watching the program so I didn't fix this.
tempIndex = currentIndex;
num--;
if (num < 0) break;
}
print(tempIndex);
Interlocked.Increment(ref currentIndex);
Thread.Sleep(1000);
}
}
private void print(int x)
{
Console.WriteLine(x);
}
}
这应该没有比赛条件。 这个想法是,对竞争值( num
)的increment
操作与检查num
值是否完成的锁在同一锁内。 在您的示例中,这是不正确的,因此可能会在不合时宜的时间进行上下文切换,以使num
更改次数超过您想要的次数。 这也是为什么我更改了Console.WriteLine()
发生位置的原因:增加了这种不适当的上下文切换的机会。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.