使用 Thread.Sleep() 时,异步编程如何与线程一起工作?

2024-05-13

假设/前言:

  1. 在之前的问题中,我们注意到Thread.Sleep阻塞线程参见:什么时候使用Task.Delay,什么时候使用Thread.Sleep? https://stackoverflow.com/questions/20082221/when-to-use-task-delay-when-to-use-thread-sleep/20084603?noredirect=1#comment127694202_20084603.
  2. 我们还注意到控制台应用程序具有三个线程:主线程、GC 线程和终结器线程 IIRC。所有其他线程都是调试器线程。
  3. 我们知道异步不会启动新线程,而是在同步上下文上运行,“仅当方法处于活动状态时才在线程上使用时间”。https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/async/task-asynchronous-programming-model https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/async/task-asynchronous-programming-model

Setup:
在示例控制台应用程序中,我们可以看到兄弟代码和父代码都不受调用的影响Thread.Sleep,至少在调用await之前(如果进一步则未知)。

var sw = new Stopwatch();
sw.Start();
Console.WriteLine($"{sw.Elapsed}");
var asyncTests = new AsyncTests();

var go1 = asyncTests.WriteWithSleep();
var go2 = asyncTests.WriteWithoutSleep();

await go1;
await go2;
sw.Stop();
Console.WriteLine($"{sw.Elapsed}");
        
Stopwatch sw1 = new Stopwatch();
public async Task WriteWithSleep()
{
    sw1.Start();
    await Task.Delay(1000);
    Console.WriteLine("Delayed 1 seconds");
    Console.WriteLine($"{sw1.Elapsed}");
    Thread.Sleep(9000);
    Console.WriteLine("Delayed 10 seconds");
    Console.WriteLine($"{sw1.Elapsed}");
    sw1.Stop();
}
public async Task WriteWithoutSleep()
{
    await Task.Delay(3000);
    Console.WriteLine("Delayed 3 second.");
    Console.WriteLine($"{sw1.Elapsed}");
    await Task.Delay(6000);
    Console.WriteLine("Delayed 9 seconds.");
    Console.WriteLine($"{sw1.Elapsed}");
}

问题: 如果线程在执行过程中被阻塞Thread.Sleep,它是如何继续处理父母和兄弟姐妹的?有些人回答说这是后台线程,但我没有看到多线程后台线程的证据。我缺少什么?


The Task.Delay https://learn.microsoft.com/en-us/dotnet/api/system.threading.tasks.task.delay方法的实现基本上是这样的(简化的):

public static Task Delay(int millisecondsDelay)
{
    var tcs = new TaskCompletionSource();
    _ = new Timer(_ => tcs.SetResult(), null, millisecondsDelay, -1);
    return tcs.Task;
}

The Task在 a 的回调上完成System.Threading.Timer组件,并根据文档 https://learn.microsoft.com/en-us/dotnet/api/system.threading.timer#remarks这个回调被调用ThreadPool https://learn.microsoft.com/en-us/dotnet/api/system.threading.threadpool thread:

该方法不在创建计时器的线程上执行;它执行在ThreadPool系统提供的线程。

所以当你等待返回的任务时Task.Delay方法,之后继续await运行于ThreadPool. The ThreadPool通常有多个线程按需立即可用,因此如果您一次创建 2 个任务(就像在示例中所做的那样),那么引入并发和并行性并不困难。控制台应用程序的主线程没有配备SynchronizationContext https://learn.microsoft.com/en-us/archive/msdn-magazine/2011/february/msdn-magazine-parallel-computing-it-s-all-about-the-synchronizationcontext默认情况下,因此没有适当的机制来阻止观察到的并发性。

¹ For demonstration purposes only. The Timer reference is not stored anywhere, so it might be garbage collected before the callback is invoked, resulting in the Task never completing.

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

使用 Thread.Sleep() 时,异步编程如何与线程一起工作? 的相关文章

随机推荐