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

JAN*_*JAN 3 .net c# 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);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

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

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

为什么?

Eri*_*ert 9

此代码是否正确同步?

没有.

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

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

代码错误地同步,这意味着有许多可能的结果.你正在采取一个事实 - 缺乏正确的同步意味着我不知道将会发生什么 - 并从中推断出一个完全不支持的结论 - 每次执行时的观察都会有所不同.从"我不知道会发生什么"中得出的正确推论是,你不知道任何执行会发生什么 ; 特别是你知道大量运行的行为会有任何特定的分布.

为什么?

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

  • @ray:谁说过比赛条件让节目变得不可预测*?不要求竞赛条件*使程序不确定.为什么有人会相信它呢? (6认同)
  • @ray:现在我很困惑.这并不奇怪,但它是*不太可能*?暂且不谈一个不可能的事件是否本质上令人惊讶的问题,谁说了*概率*?**线程调度程序不需要具有概率行为**.算法*可以*选择任何线程,当然.它*确实*选择一个线程.**为什么有人会期望它会随机**?同样,我们从一个无知的位置开始 - 我不知道调度程序是如何工作的 - 并从中推断出"因此它必须是随机的"? (4认同)
  • @ron:你仍然没有提供理由说明为什么你认为你应该得到不同的结果.告诉你什么,我们会玩游戏.你让我挑选一到十分之一的数字.我们将打10万轮.我每次都可以选7次吗?如果每场比赛的结果都是我选7,你会感到惊讶吗? (3认同)
  • @ray:再说一次,假设我们玩一个游戏,你问我1到10号.你知道*我会说些什么号码吗?不,你应该从中推断出我将产生一个均匀分布的随机序列吗?绝对不!如果我每次问你都说7,那应该不会令人惊讶,因为很明显7是最好的数字. (3认同)

Eni*_*ity 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;
}
Run Code Online (Sandbox Code Playgroud)

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

结果

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