Code:
using System;
using System.Collections.Generic;
using System.Data;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace SampleAsyncConsoleProgram
{
class Program
{
public static void ConsolePrint(string line)
{
Console.WriteLine(DateTime.Now.ToString("HH:mm:ss.fff") + " ["
+ Thread.CurrentThread.ManagedThreadId.ToString() + "] > " + line);
}
static readonly IEnumerable<string> s_urlList = new string[]
{
"website1",
"website2",
"website3",
"website4"
};
static Task Main() => DownloadWebsites();
static async Task DownloadWebsites()
{
ConsolePrint("Main program: Program started..");
var stopwatch = Stopwatch.StartNew();
ConsolePrint("Main program: Adding tasks to list..");
IEnumerable<Task> downloadTasksQuery =
from url in s_urlList
select ProcessUrlAsync(url);
List<Task> downloadTasks = downloadTasksQuery.ToList();
ConsolePrint("Main program: Added tasks to list..");
while (downloadTasks.Any())
{
ConsolePrint("Main program: Checking if a task is completed.... followed by await...");
Task finishedTask = await Task.WhenAny(downloadTasks);
ConsolePrint("Main program: A task was completed..");
downloadTasks.Remove(finishedTask);
await finishedTask;
}
stopwatch.Stop();
ConsolePrint($"Main program: Program Completed Elapsed time: {stopwatch.Elapsed}\n Current time is " + DateTime.Now);
}
static async Task ProcessUrlAsync(string url)
{
ConsolePrint("Task: Starts downloading " + url);
await Task.Delay(5000); //represents async call to fetch url
ConsolePrint("Task: Sleeping for 10 sec.." + url);
Thread.Sleep(10000); //represents some long running blocking synchronous work and keeping thread busy...
ConsolePrint("Task: Wake up.." + url);
ConsolePrint("Task: Done Task.." + url);
}
}
}
Output:
13:39:24.567 [1] > Main program: Program started..
13:39:36.255 [1] > Main program: Adding tasks to list..
13:39:37.177 [1] > Task: Starts downloading website1
13:39:43.241 [1] > Task: Starts downloading website2
13:39:43.242 [1] > Task: Starts downloading website3
13:39:43.242 [1] > Task: Starts downloading website4
13:39:43.243 [1] > Main program: Added tasks to list..
13:39:43.243 [1] > Main program: Checking if a task is completed.... followed by await...
13:39:48.811 [5] > Task: Sleeping for 10 sec..website1
13:39:48.810 [7] > Task: Sleeping for 10 sec..website2
13:39:48.810 [4] > Task: Sleeping for 10 sec..website4
13:39:48.810 [6] > Task: Sleeping for 10 sec..website3
13:39:58.823 [4] > Task: Wake up..website4
13:39:58.826 [5] > Task: Wake up..website1
13:39:58.823 [6] > Task: Wake up..website3
13:39:58.826 [7] > Task: Wake up..website2
13:39:58.828 [5] > Task: Done Task..website1
13:39:58.828 [6] > Task: Done Task..website3
13:39:58.829 [7] > Task: Done Task..website2
13:39:58.828 [4] > Task: Done Task..website4
13:39:58.922 [7] > Main program: A task was completed..
13:39:58.923 [7] > Main program: Checking if a task is completed.... followed by await...
13:39:58.923 [7] > Main program: A task was completed..
13:39:58.923 [7] > Main program: Checking if a task is completed.... followed by await...
13:39:58.924 [7] > Main program: A task was completed..
13:39:58.924 [7] > Main program: Checking if a task is completed.... followed by await...
13:39:58.924 [7] > Main program: A task was completed..
13:39:58.985 [7] > Main program: Program Completed Elapsed time: 00:00:22.6686293
Current time is 30/07/2021 13:39:58
我期待的是 - 在第 65 行(在代码中),Thread.Sleep(10000);
- 每个任务应该独立阻塞 10 秒(因为我已经使用Thread.Sleep(10000)
这是同步和阻塞代码)。
然而从上面的输出来看,它看起来像Thread.Sleep
(阻塞操作)就像多线程一样发生。
我明白 - async+await 没有Task.Run()
对于 Windows 应用程序,使用相同的线程(UI 线程)。然后Thread.Sleep
是一个同步阻塞操作。
-
那么为什么每个Task
不阻塞10秒?以及如何使每个Task
封锁10秒?
-
我被告知此行为与同步上下文有关。因此它从线程池中提取线程(与 Windows 窗体应用程序的情况不同)。那么我想问的是——这是否意味着它将执行多线程(多个线程并行运行)?
await Task.Delay(5000);
在模拟 IO 密集型操作方面做得非常好。尽管 IO 绑定操作不使用线程来等待完成(请参阅没有线程 https://blog.stephencleary.com/2013/11/there-is-no-thread.html斯蒂芬·克利里 (Stephen Cleary) 的文章,也docs https://learn.microsoft.com/en-us/dotnet/standard/async-in-depth可以阐明一些),延续将在线程池上运行。所以downloadTasksQuery.ToList()
将开始你所有的await Task.Delay
然后是并行的(取决于任务和线程池的数量以及SynchronizationContext
)其中部分或全部设置可以在单独的线程上继续。
那么为什么每个Task都没有阻塞10秒呢?那么如何让每个任务阻塞 10 秒呢?
它会阻塞,但在您的情况下它会阻塞一个单独的线程。
我被告知此行为与同步上下文有关。
是的,此行为可能会受到同步上下文的影响。例如,在桌面应用程序中,未标记的延续ConfigureAwait(false)
将在单个 UI 线程上运行,因为您没有ConfigureAwait(false)
配置为await Task.Delay(5000)
实际上,您最终会导致 UI 无响应。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)