我因为无聊而从维基百科上检索随机文章玩了一段时间。首先我写了这段代码:
private async void Window_Loaded(object sender, RoutedEventArgs e)
{
await DownloadAsync();
}
private async Task DownloadAsync()
{
Stopwatch sw = new Stopwatch();
sw.Start();
var tasks = new List<Task>();
var result = new List<string>();
for (int index = 0; index < 60; index++)
{
var task = Task.Run(async () => {
var scheduledAt = DateTime.UtcNow.ToString("mm:ss.fff");
using (var client = new HttpClient())
using (var response = await client.GetAsync("https://en.wikipedia.org/wiki/Special:Random"))
using (var content = response.Content)
{
var page = await content.ReadAsStringAsync();
var receivedAt = DateTime.UtcNow.ToString("mm:ss.fff");
var data = $"Job done at thread: {Thread.CurrentThread.ManagedThreadId}, Scheduled at: {scheduledAt}, Recieved at: {receivedAt} {page}";
result.Add(data);
}
});
tasks.Add(task);
}
await Task.WhenAll(tasks.ToArray());
sw.Stop();
Console.WriteLine($"Process took: {sw.Elapsed.Seconds} sec {sw.Elapsed.Milliseconds} ms");
foreach (var item in result)
{
Debug.WriteLine(item);
}
}
但是我想要摆脱这个异步匿名方法:Task.Run(async () => ...
,因此我将代码的相关部分替换为以下内容:
for (int index = 0; index < 60; index++)
{
var task = Task.Run(() => {
var scheduledAt = DateTime.UtcNow.ToString("mm:ss.fff");
using (var client = new HttpClient())
// Get this synchronously.
using (var response = client.GetAsync("https://en.wikipedia.org/wiki/Special:Random").Result)
using (var content = response.Content)
{
// Get this synchronously.
var page = content.ReadAsStringAsync().Result;
var receivedAt = DateTime.UtcNow.ToString("mm:ss.fff");
var data = $"Job done at thread: {Thread.CurrentThread.ManagedThreadId}, Scheduled at: {scheduledAt}, Recieved at: {receivedAt} {page}";
result.Add(data);
}
});
tasks.Add(task);
}
我原以为,将异步代码替换为同步代码时,程序的表现应该完全一样,因为我已经在任务中封装了异步代码。这样,我可以确保任务调度程序(WPF任务调度程序)会将其排队到ThreadPool的某个空闲线程上。正如我所看到的返回结果一样:
Job done at thread: 6, Scheduled at: 53:57.534, Recieved at: 54:54.545 ...
Job done at thread: 21, Scheduled at: 54:06.742, Recieved at: 54:54.574 ...
Job done at thread: 41, Scheduled at: 54:26.742, Recieved at: 54:54.576 ...
Job done at thread: 10, Scheduled at: 53:59.018, Recieved at: 54:54.614 ...
问题在于第一段代码需要执行约6秒钟,而第二段同步的.Result
则需要约50秒钟。当任务数量减少时,差异变得更小。有没有人能解释为什么它们需要这么长时间,即使它们在单独的线程上执行并且执行完全相同的单个操作?
.Result
会导致未定义行为,你绝不应该这样做;它可能被卡在同步上下文中,也可能在等待线程池增长(从内存中最多每秒一个) - 因为所有现有的线程池线程都正在阻塞等待.Result
,谁知道会发生什么? - Marc Gravell.Result
通常会导致死锁。您在同步上下文线程上,并且阻塞等待.Result
;当值可用时,完成尝试发生,需要通过同步上下文进行 - 这会阻塞等待同步线程。这已经被阻塞等待.Result
。 - Marc GravellHttpClient
是我首先想到的。 - FCin