Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/269.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C#线程:竞争条件示例_C#_Multithreading - Fatal编程技术网

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
时出现竞争条件,则
++
运算符的内部操作将类似于:-

  • 第一个线程读取值0
  • 第二个线程读取值0
  • 第一个线程将值增加到1
  • 第二个线程将值增加到1
  • 第一个线程写入值1
  • 第二个线程写入值1
  • 操作3到6的顺序并不重要,关键是当变量的值为x时,读取操作1和2都可能发生,导致对y的增量相同,而不是每个线程对不同的x和y值执行增量

    这可能导致以下输出:-

    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
    
    更糟糕的是:-

  • 第一个线程读取值0
  • 第二个线程读取值0
  • 第二个线程将值增加到1
  • 第二个线程写入值1
  • 第二个线程读取值1
  • 第二个线程将值增加到2
  • 第二个线程写入值2
  • 第一个线程将值增加到1
  • 第一个线程写入值1
  • 第二个线程读取值1
  • 第二个线程将值增加到2
  • 第二个线程写入值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毫秒的起始时间,您会期望在出现任何“混乱”之前,在相当多的迭代中看到锁步交替。