为什么我会使用此非同步代码重复获得相同的结果?

时间:2015-11-07 07:29:15

标签: c# .net multithreading

考虑一下:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace Multithreading
{
    class Program
    {
        static int result = 0;

        static void changeResult1()
        {
            result = 1;
        }

        static void changeResult2()
        {
            result = 2;
        }

        static void changeResult3()
        {
            result = 3;
        }

        static void Main(string[] args)
        {

            Thread t1 = new Thread(new ThreadStart(changeResult1));
            Thread t2 = new Thread(new ThreadStart(changeResult2));
            Thread t3 = new Thread(new ThreadStart(changeResult3));


            t1.Start();
            t2.Start();
            t3.Start();
            Console.WriteLine(result);
        }
    }
}

我很确定这段代码是不同步的,这意味着result每次执行代码(0,1,2,3)都应该不同。从我的观点来看,如果主线程在任何一个线程开始之前完成,结果甚至可以是0

但是我在屏幕上反复得到2

为什么?

2 个答案:

答案 0 :(得分:9)

  

此代码是否正确同步?

没有

  

意味着每次执行代码(0,1,2,3)时结果应该不同。

为什么这是真的?您没有为此声明提供任何理由。

代码未正确同步,这意味着有许多可能的结果。你正在接受一个事实 - 缺乏正确的同步意味着我不知道将会发生什么 - 并从中推断出一个完全不支持的结论 - 每个观察结果会有所不同执行即可。 “我不知道会发生什么”的正确推论是你不知道任何执行会发生什么;特别是你知道大量运行中的行为将具有任何特定分布。

  

为什么?

为什么?你注意到2是可能的结果,你得到了一个可能的结果。你做两次同样的事情,结果相同;这并不奇怪。仅仅因为运行时允许产生许多不同的结果并不意味着它必须产生许多不同的结果。两次做同样的事情通常会产生非常相似的结果。

答案 1 :(得分:8)

在我看来,观察到的结果非常合理。

启动线程是一项昂贵的操作。我希望每个线程的启动时间大大超过实际运行分配给每个线程的代码所花费的时间。

因此,一旦设置了线程1,主线程就会在线程1执行时继续设置线程2。线程1在线程2准备好运行之前很久就完成了。

与线程2&线程3。

因此,一旦设置了第三个线程,线程2就已经完成了,并且主线程立即移动到Console.WriteLine(result);。这是之前线程3实际上已经开始,并且在线程2完成后是一个很长很长的时间。

因此,当然,结果几乎总是2

为了支持我不科学的分析,我想我可能会添加一些计时代码,看看我是否是正确的。

void Main()
{
    times[0] = sw.Elapsed.TotalMilliseconds;
    Thread t1 = new Thread(new ThreadStart(changeResult1));
    times[1] = sw.Elapsed.TotalMilliseconds;
    Thread t2 = new Thread(new ThreadStart(changeResult2));
    times[2] = sw.Elapsed.TotalMilliseconds;
    Thread t3 = new Thread(new ThreadStart(changeResult3));
    times[3] = sw.Elapsed.TotalMilliseconds;

    t1.Start();
    times[4] = sw.Elapsed.TotalMilliseconds;
    t2.Start();
    times[5] = sw.Elapsed.TotalMilliseconds;
    t3.Start();
    times[6] = sw.Elapsed.TotalMilliseconds;
    var r = result.ToString();
    times[7] = sw.Elapsed.TotalMilliseconds;
    Console.WriteLine(result);
    times[8] = sw.Elapsed.TotalMilliseconds;

    Thread.Sleep(1000);
    times
        .Select((x, n) => new { t = (x - times[0]).ToString("0.000"), n})
        .OrderBy(x => x.t)
        .Dump();
}

static Stopwatch sw = Stopwatch.StartNew();
static double[] times = new double[15];

static int result = 0;

static void changeResult1()
{
    times[9] = sw.Elapsed.TotalMilliseconds;
    result = 1;
    times[10] = sw.Elapsed.TotalMilliseconds;
}

static void changeResult2()
{
    times[11] = sw.Elapsed.TotalMilliseconds;
    result = 2;
    times[12] = sw.Elapsed.TotalMilliseconds;
}

static void changeResult3()
{
    times[13] = sw.Elapsed.TotalMilliseconds;
    result = 3;
    times[14] = sw.Elapsed.TotalMilliseconds;
}

你必须稍微关注弹跳球,但是这样的一段代码产生了这个输出:

results

这清楚地表明在t3.Start();之后执行的代码是var r = result.ToString();而不是result = 3;。更重要的是result = 2;在线程3开始之前发生了很长时间。