C#线程:竞争条件示例
我正在读书 第一个示例如下所示:C#线程:竞争条件示例,c#,multithreading,C#,Multithreading,我正在读书 第一个示例如下所示: public class FirstUnsyncThreads { private int i = 0; public static void Main (string[] args) { FirstUnsyncThreads myThreads = new FirstUnsyncThreads (); } public FirstUnsyncThreads () { // Creating o
public class FirstUnsyncThreads {
private int i = 0;
public static void Main (string[] args) {
FirstUnsyncThreads myThreads = new FirstUnsyncThreads ();
}
public FirstUnsyncThreads () {
// Creating our two threads. The ThreadStart delegate is points to
// the method being run in a new thread.
Thread firstRunner = new Thread (new ThreadStart (this.firstRun));
Thread secondRunner = new Thread (new ThreadStart (this.secondRun));
// Starting our two threads. Thread.Sleep(10) gives the first Thread
// 10 miliseconds more time.
firstRunner.Start ();
Thread.Sleep (10);
secondRunner.Start ();
}
// This method is being excecuted on the first thread.
public void firstRun () {
while(this.i < 10) {
Console.WriteLine ("First runner incrementing i from " + this.i +
" to " + ++this.i);
// This avoids that the first runner does all the work before
// the second one has even started. (Happens on high performance
// machines sometimes.)
Thread.Sleep (100);
}
}
// This method is being excecuted on the second thread.
public void secondRun () {
while(this.i < 10) {
Console.WriteLine ("Second runner incrementing i from " + this.i +
" to " + ++this.i);
Thread.Sleep (100);
}
}
}
哇,这是什么?不幸的是,文章中的解释对我来说是不够的。你能解释一下为什么增量顺序混乱吗
谢谢 当存在多个线程时,同步是必不可少的。在本例中,您会看到两个线程都在读写
this.i
,但在同步这些访问时没有做任何好的尝试。由于它们都同时修改相同的内存区域,因此可以观察到混乱的输出。
呼叫睡眠是危险的,这是一种导致确定错误的方法。您不能假设线程总是被初始的10毫秒替换
简而言之:不要使用睡眠进行同步:-),而是采用某种线程同步技术(例如锁、互斥、信号量)。始终尝试使用最轻的锁,以满足您的需要
Joe Duffy的《Windows上的并发编程》一书是一个有用的资源。增量不是无序发生的,而是控制台。WriteLine(…)将多个线程的输出写入单线程控制台,而从多个线程到一个线程的同步会导致消息出现无序 我假设这个例子试图创建一个竞争条件,但在您的例子中失败了。不幸的是,并发问题,如竞争条件和死锁,由于其性质,很难预测和重现。您可能希望尝试运行它几次,修改它以使用更多线程,并且每个线程应该增加更多的次数(比如100000次)。然后您可能会看到,最终结果将不等于所有增量之和(由竞争条件引起)。当我运行这个(在双核上)时,我的输出是
First runner incrementing i from 0 to 1
Second runner incrementing i from 1 to 2
First runner incrementing i from 2 to 3
Second runner incrementing i from 3 to 4
First runner incrementing i from 4 to 5
Second runner incrementing i from 5 to 6
First runner incrementing i from 6 to 7
Second runner incrementing i from 7 to 8
First runner incrementing i from 8 to 9
Second runner incrementing i from 9 to 10
如我所料。您正在运行两个循环,都执行Sleep(100)。这是非常不适合证明比赛条件
代码确实有竞争条件(正如VoteyDisciple所描述的),但它不太可能出现
我无法解释您的输出中缺少顺序(它是真实的输出吗?),但Console类将同步输出调用
如果不使用Sleep()调用并运行循环1000次(而不是10次),您可能会看到两个运行程序都从554递增到555或其他什么。我认为本文作者把事情搞糊涂了 VoteyDisciple是正确的,即
++i
不是原子的,如果目标在操作过程中未锁定,则可能发生竞争条件,但这不会导致上述问题
如果调用++i
时出现竞争条件,则++
运算符的内部操作将类似于:-
First runner incrementing i from 0 to 1
Second runner incrementing i from 0 to 1
First runner incrementing i from 0 to 1
Second runner incrementing i from 0 to 1
Second runner incrementing i from 1 to 2
Second runner incrementing i from 1 to 2
更糟糕的是:-
First runner incrementing i from 0 to 1
Second runner incrementing i from 0 to 1
First runner incrementing i from 0 to 1
Second runner incrementing i from 0 to 1
Second runner incrementing i from 1 to 2
Second runner incrementing i from 1 to 2
等等
此外,在读取i
和执行++i
之间可能存在竞争条件,因为Console.WriteLine调用将i
和++i
连接起来。这可能导致如下输出:-
First runner incrementing i from 0 to 1
Second runner incrementing i from 1 to 3
First runner incrementing i from 1 to 2
作者描述的混乱控制台输出只能由控制台输出的不可预测性导致,与
i
变量上的竞争条件无关。在执行++i
或连接i
和++i
时锁定i
不会改变这种行为。,原因是Console的同步内部。抱歉,Console.WriteLine。更正。我不认为Thread.Sleep()用于尝试同步线程。它只是为了让增量可以被观察到(否则它只会同时出现在控制台上),这有一个副作用,即将竞争条件的可能性降低到几乎为零。是的,评论说,但实际上它被用作一种粗略的同步技术,看看两次调用Start()之间的10毫秒间隔. 在缺乏其他技术的情况下,在我看来,睡眠是在假装两个线程之间的同步。+1对于Joe Duffy的书来说,这是Windows并发Bible。文章的作者说,输出“可能”如图所示。这是正确的,因为您无法证明输出不会如图所示。然而,在实践中,假设第一个大约有10毫秒的起始时间,您会期望在出现任何“混乱”之前,在相当多的迭代中看到锁步交替。