以下代码应该(至少我认为)创建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-600毫秒)- 在控制台中,mono显示了许多创建的线程:
已加载程序集:/Users/xxxxx/Programming/xxxxx/xxxxxxxxxx/bin/Release/xxxxx.exe
线程已启动:#2
线程已启动:#3
线程已启动:#4
线程已启动:#5
线程已启动:#6
线程已启动:#7
线程已完成:#3 <--显然1000ms的延迟已经完成了吗?
线程已完成:#2 <-- 显然1000ms的延迟已经完成了吗?
线程已启动:#8
线程已启动:#9
线程已启动:#10
线程已启动:#11
线程已启动:#12
线程已启动:#13
...你懂的。
这实际上是一个错误吗?还是我使用库的方式不对?
[编辑] 我使用计时器测试了自定义的睡眠方法:
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上测试了原始代码(使用Task.Delay
),并且它按预期运行(1000个Tasks
,并行等待1秒钟并完成)。
所以答案是:Mono对某些方法的实现并不完美。通常情况下,Task.Delay
不会启动线程,即使有很多线程也不应该创建多个线程。
Task.Delay
操作本身不会创建新线程,但在延迟结束后执行Console.WriteLine
的回调方法肯定需要在线程上执行。如果所有延迟都相同,则所有这些回调将大致同时发生。尝试在示例中随机延迟并查看是否使用更少的线程。 - Sam Harwell