下面的代码应该(至少在我看来)创建 100 个 Tasks
,它们都在并行等待(这就是并发性的重点,对吧:D?)并且几乎同时完成。我猜每个 Task.Delay
都会在内部创建一个 Timer
对象。
public static async Task MainAsync() {
var tasks = new List<Task>();
for (var i = 0; i < 100; i++) {
Func<Task> func = async () => {
await Task.Delay(1000);
Console.WriteLine("Instant");
};
tasks.Add(func());
}
await Task.WhenAll(tasks);
}
public static void Main(string[] args) {
MainAsync().Wait();
}
但!当我在 Mono 上运行它时,我得到了非常奇怪的行为:
Tasks
没有同时完成,有巨大的延迟(大概500-600ms) 加载的程序集:/Users/xxxxx/Programming/xxxxx/xxxxxxxxxx/bin/Release/xxxxx.exe
话题开始:#2
话题开始:#3
话题开始:#4
话题开始:#5
话题开始:#6
话题开始:#7
线程完成:#3
线程完成:#2
话题开始:#8
话题开始:#9
话题开始:#10
话题开始:#11
话题开始:#12
话题开始:#13
... 你懂了。
这实际上是一个错误吗?还是我使用的图书馆错了?
[编辑]
我使用 Timer 测试了自定义 sleep 方法:
public static async Task MainAsync() {
Console.WriteLine("Started");
var tasks = new List<Task>();
for (var i = 0; i < 100; i++) {
Func<Task> func = async () => {
await SleepFast(1000);
Console.WriteLine("Instant");
};
tasks.Add(func());
}
await Task.WhenAll(tasks);
Console.WriteLine("Ready");
}
public static Task SleepFast(int amount) {
var source = new TaskCompletionSource<object>();
new Timer(state => {
var oldSrc = (TaskCompletionSource<object>)state;
oldSrc.SetResult(null);
}, source, amount, 0);
return source.Task;
}
这一次,所有任务瞬间完成。所以,我认为这是一个非常糟糕的实现或错误。
[编辑2]
仅供引用:我现在使用 Windows 8.1 在 .NET 上测试了原始代码(使用
Task.Delay
),它按预期运行(1000 Tasks
,并行等待 1 秒并完成)。所以答案是:Mono 的实现。的(某些)方法并不完美。一般来说
Task.Delay
不会启动一个线程,甚至很多都不应该创建多个线程。 最佳答案
Task
库更多地设计用于在不阻塞整个工作流的情况下管理阻塞任务(任务异步,Microsoft 混淆地称为“任务并行”),而 而不是 用于执行大型并发计算块)(并行执行)。
任务库使用调度程序并将准备执行的作业排队。当作业运行时,它们将在线程池线程上运行,并且数量非常有限。有扩展线程数的逻辑,但除非您有数百个 CPU 内核,否则它将保持较低的数量。
所以为了回答这个问题,你的一些任务排队等待池中的线程,而其他延迟任务已经由调度程序发出。
调度程序和线程池逻辑可以在运行时更改,但如果您试图快速完成大量计算,则 Task
不适合这项工作。如果您想处理大量慢速资源(如磁盘、数据库或 Internet 资源),Task
可能有助于保持应用程序响应。
如果你只是想了解 Task
试试这些:
关于c# - Task.Delay 是否启动一个新线程?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/21878050/