假設/前奏:
- 在前面的問題中,我們注意到 thread.sleep 會阻塞執行緒,請參閱: 何時使用 Task.Delay,何時使用 Thread.Sleep?.
- 我們還注意到控制臺應用程式具有三個執行緒:主執行緒、GC 執行緒和終結器執行緒 IIRC。所有其他執行緒都是除錯器執行緒。
- 我們知道 async 不會啟動新執行緒,而是在同步背景關系中運行,“僅當方法處于活動狀態時才在執行緒上使用時間”。 https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/async/task-asynchronous-programming-model
設定:
在示例控制臺應用程式中,我們可以看到兄弟代碼和父代碼都不會受到對 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期間被阻止執行,它如何繼續處理父級和兄弟級?有人回答說它是后臺執行緒,但我沒有看到多執行緒后臺執行緒的證據。我錯過了什么?
uj5u.com熱心網友回復:
我沒有看到多執行緒后臺執行緒的證據。我錯過了什么?
可能你找錯地方了,或者使用了錯誤的工具。有一個方便的屬性可能對您有用,格式為Thread.CurrentThread.ManagedThreadId. 根據檔案,
執行緒的 ManagedThreadId 屬性值用于在其行程中唯一標識該執行緒。
ManagedThreadId 屬性的值不隨時間變化
這意味著在同一執行緒上運行的所有代碼將始終看到相同的ManagedThreadId值。如果您在代碼中添加一些額外WriteLine的 s,您將能夠看到您的任務在其生命周期中可能在多個不同的執行緒上運行。一些異步應用程式甚至完全有可能讓它們的所有任務在同一個執行緒上運行,盡管在正常情況下您可能不會在代碼中看到這種行為。
這是我機器上的一些示例輸出,不保證在你的機器上是相同的,也不一定在同一應用程式的連續運行中是相同的輸出。
00:00:00.0000030
* WriteWithSleep on thread 1 before await
* WriteWithoutSleep on thread 1 before first await
* WriteWithSleep on thread 4 after await
Delayed 1 seconds
00:00:01.0203244
* WriteWithoutSleep on thread 5 after first await
Delayed 3 second.
00:00:03.0310891
* WriteWithoutSleep on thread 6 after second await
Delayed 9 seconds.
00:00:09.0609263
Delayed 10 seconds
00:00:10.0257838
00:00:10.0898976
在執行緒上運行任務的業務由TaskScheduler. 您可以撰寫一個強制代碼為單執行緒的代碼,但這通常不是一件有用的事情。默認調度程式使用執行緒池,因此可以在許多不同的執行緒上運行此類任務。
uj5u.com熱心網友回復:
該Task.Delay方法基本上是這樣實作的(簡化的1):
public static Task Delay(int millisecondsDelay)
{
var tcs = new TaskCompletionSource();
_ = new Timer(_ => tcs.SetResult(), null, millisecondsDelay, -1);
return tcs.Task;
}
在組件的Task回呼中完成System.Threading.Timer,并且根據檔案ThreadPool,此回呼是在執行緒上呼叫的:
該方法不會在創建計時器的執行緒上執行;它在
ThreadPool系統提供的執行緒上執行。
因此,當您等待Task.Delay方法回傳的任務時,await在ThreadPool. 通常ThreadPool有多個執行緒可立即按需使用,因此如果您一次創建 2 個任務,引入并發性和并行性并不困難,就像您在示例中所做的那樣。默認情況下,控制臺應用程式的主執行緒沒有配備 a SynchronizationContext,因此沒有適當的機制來防止觀察到的并發。
1僅用于演示目的。參考沒有存盤在Timer任何地方,因此它可能在呼叫回呼之前被垃圾收集,導致Task永遠不會完成。
uj5u.com熱心網友回復:
我不接受我自己的答案,我會接受別人的答案,因為他們幫助我解決了這個問題。首先,在我的問題的背景關系中,我使用的是 async Main。在 Theodor 和 Rook 的答案之間很難做出選擇。但是,Rook 的回答為我提供了幫助我釣魚的一件事:Thread.CurrentThread.ManagedThreadId
這些是我運行代碼的結果:
1 00:00:00.0000767
Not Delayed.
1 00:00:00.2988809
Delayed 1 second.
4 00:00:01.3392148
Delayed 3 second.
5 00:00:03.3716776
Delayed 9 seconds.
5 00:00:09.3838139
Delayed 10 seconds
4 00:00:10.3411050
4 00:00:10.5313519
我注意到這里有 3 個執行緒,初始執行緒 (1) 提供第一個呼叫方法和 WriteWithSleep() 的一部分,直到 Task.Delay 被初始化并稍后等待。當 Task.Delay 被帶回執行緒 1 時,所有內容都在執行緒 4 上運行,而不是執行緒 1 上的 main 和 WriteWithSleep 的其余部分。
WriteWithoutSleep 使用自己的 Thread(5)。
所以我的錯誤是認為只有 3 個執行緒。我相信這個問題的答案: https ://stackoverflow.com/questions/3476642/why-does-this-simple-net-console-app-have-so-many-threads#:~:text=You should only see three,see are debugger-related threads。
但是,該問題可能不是異步的,或者可能沒有考慮執行緒池中的這些額外作業執行緒。
感謝大家幫助解決這個問題。
轉載請註明出處,本文鏈接:https://www.uj5u.com/qukuanlian/478919.html
