我是线程新手。所以,我只是一个接一个地创建了4个专用线程(而不是使用Tasks),并为它们提供了一些工作。但是,它让我怀疑startProcessing函数中的竞争条件,我只是将currentIndex上的值存储到lock中的局部变量。但是,我跑了很多次,但不能满足没有种族问题或其他任何问题。
请帮我清除这件事。
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);
}
}
答案 0 :(得分:1)
考虑startProcessing
方法。想象一下,所有四个线程一起到达while
支票。由于循环检查未同步,因此当num
为2时,所有四个都可能进入循环。
while
检查。见号num&gt; 0.进入循环。发布背景。while
检查。 Num仍为2,因此&gt; 0. while
检查的保护。递减操作都将很好地原子化和同步,但是当四个操作完成时,你仍然处于-2。你有&gt; 0测试,但它在锁外执行。因此,您的currentIndex
可能会增加1000倍以上 - 我认为,对于可能不必要地进入循环的每个额外线程,可增加1003倍。答案 1 :(得分:0)
我稍微修改了您的原始程序(在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);
}
}
这个应该没有竞争条件。这个想法是对争用值(increment
)的num
操作在与检查值num
完成的内容相同的锁内。在您的示例中,这不是真的,因此可能会在不合适的时间进行上下文切换,以使num
更改次数超出您的预期。这也是为什么我在Console.WriteLine()
发生变化的原因背后的想法:增加不合时宜的上下文切换的机会。