Thread.Sleep(0) 在 .NET Core 3.1 上没有按预期工作

Mes*_*ros 3 .net c# multithreading .net-core

因此,我正在根据以下书籍学习 .NET Thread 类:Exam Ref 70-483: Programming in C#,我注意到一些我不知道为什么会发生或是否应该发生的事情。

这本书在第 4 页给了我这部分代码:

public static class Program
{
   public static void ThreadMethod()
   {
       for (int i = 0; i < 10; i++)
       {
           Console.WriteLine("ThreadProc: {0}", i);
           Thread.Sleep(0);
       }
   }
   public static void Main()
   {
       Thread t = new Thread(new ThreadStart(ThreadMethod));
       t.Start();
       for (int i = 0; i < 4; i++)
       {
           Console.WriteLine("Main thread: Do some work.");
           Thread.Sleep(0);
       }
       t.Join();
   }
}
Run Code Online (Sandbox Code Playgroud)

预期结果如下所示,因为在每个 Thread.Sleep(0) 处,当前线程退出并继续处理队列中的下一个线程:

// Main thread: Do some work.  
// ThreadProc: 0  
// Main thread: Do some work.  
// ThreadProc: 1  
// Main thread: Do some work.  
// ThreadProc: 2  
// Main thread: Do some work.  
// ThreadProc: 3  
// ThreadProc: 4  
// ThreadProc: 5  
// ThreadProc: 6  
// ThreadProc: 7  
// ThreadProc: 8  
// ThreadProc: 9  
Run Code Online (Sandbox Code Playgroud)

但是当我构建我的这个版本时,它没有按预期工作,即使我复制粘贴代码并在我的机器上运行,每次执行程序时它都会给出这个结果,上面的代码相同

// Main thread: Do some work.  
// ThreadProc: 0  
// ThreadProc: 1  
// ThreadProc: 2  
// ThreadProc: 3  
// ThreadProc: 4  
// ThreadProc: 5  
// ThreadProc: 6  
// ThreadProc: 7  
// ThreadProc: 8  
// ThreadProc: 9  
// Main thread: Do some work.  
// Main thread: Do some work.  
// Main thread: Do some work.  
Run Code Online (Sandbox Code Playgroud)

如您所见,它似乎进入了副线程,当我调用 Thread.Sleep(0) 方法时,它不会像它应该的那样返回主线程,而是继续在副线程上工作直到它完成。

作为一种解决方法,我使用 Random 类生成一个 100 到 500 之间的随机数以传递给 Thread.Sleep() 方法,它开始按预期工作:

public static class Program
{
    public static void ThreadMethod()
    {
        var rand = new Random();
        for (int i = 0; i < 10; i++)
        {
            Console.WriteLine("ThreadProc: {0}", i);
            Thread.Sleep(rand.Next(100, 500));
        }
    }
    public static void Main()
    {
        Thread t = new Thread(new ThreadStart(ThreadMethod));
        t.Start();
        var rand = new Random();
        for (int i = 0; i < 4; i++)
        {
            Console.WriteLine("Main thread: Do some work.");
            Thread.Sleep(rand.Next(100, 500));
        }
        t.Join();
    }
}
Run Code Online (Sandbox Code Playgroud)

结果如下所示:

// Main thread: Do some work.  
// ThreadProc: 0  
// ThreadProc: 1  
// ThreadProc: 2  
// Main thread: Do some work.  
// ThreadProc: 3  
// Main thread: Do some work.  
// ThreadProc: 4  
// Main thread: Do some work.  
// ThreadProc: 5  
// ThreadProc: 6  
// ThreadProc: 7  
// ThreadProc: 8  
// ThreadProc: 9  
Run Code Online (Sandbox Code Playgroud)

我不认为这本书是在 .NET Core 框架上写的,我认为这个“问题”可能是这两个框架之间同步上下文的变化引起的。我检查了两个线程的优先级并设置为默认的“正常”。

任何人都知道为什么会发生这种情况?

Kev*_*sse 5

对于此示例,您不会看到 .net 框架和 .net 核心之间的任何行为差异。

这本书是在大多数消费级 CPU 是单核的时代写成的。对于单核 CPU,一次只能调度一个线程,因此样本的输出是可预测的。

现在,几乎所有的 CPU 都有多个内核,这意味着可以同时调度多个线程。这使得程序的输出不可预测,并且不受调用的影响Thread.Sleep(0)

获得本书关于现代硬件的行为的一种方法是设置进程的亲和性,使其在单核上运行:

> start /affinity 1 CoreConsoleApp1.exe
Run Code Online (Sandbox Code Playgroud)

通过这样做,我得到了预期的交错输出:

ThreadProc: 0
Main thread: Do some work.
ThreadProc: 1
Main thread: Do some work.
ThreadProc: 2
Main thread: Do some work.
ThreadProc: 3
Main thread: Do some work.
ThreadProc: 4
ThreadProc: 5
ThreadProc: 6
ThreadProc: 7
ThreadProc: 8
ThreadProc: 9
Run Code Online (Sandbox Code Playgroud)