[英]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.