【发布时间】:2014-03-19 15:21:03
【问题描述】:
以下代码应该(至少在我看来)创建 100 个Tasks,它们都在并行等待(这就是并发的重点,对吧 :D ?)并且几乎同时完成。我猜每个Task.Delay 都会在内部创建一个Timerobject。
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 毫秒) - 在控制台单声道中显示了很多创建的线程:
加载的程序集:/Users/xxxxx/Programming/xxxxx/xxxxxxxxxx/bin/Release/xxxxx.exe
线程开始:#2
线程开始:#3
线程开始:#4
线程开始:#5
线程开始:#6
线程开始:#7
Thread finished: #3
Thread finished: #2
线程开始:#8
线程开始:#9
线程开始:#10
线程开始:#11
线程开始:#12
线程开始:#13
...你明白了。
这实际上是一个错误吗?还是我用错了图书馆?
[编辑] 我使用 Timer 测试了自定义睡眠方法:
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 的 impl。 (某些)方法并不完美。通常Task.Delay 不会启动线程,甚至其中很多也不应该创建多个线程。
【问题讨论】:
-
Task.Delay 在内部使用一个计时器,该计时器在内部使用线程池。由于您目前正在非常快速地创建 100 个任务,我猜您的线程池中的线程即将用完,因此会自动创建新线程。
-
那么计时器不是使用可扩展技术实现的吗?我希望有一些非常有效的东西。例如,在 node.js 中,我相信他们使用 epoll(用于 linux)。
-
每个任务间隔 500-600 毫秒?你能发布确切的时间吗?
-
Task.Delay操作本身不会创建新线程,但延迟结束后执行Console.WriteLine的回调方法肯定需要在线程上执行。如果所有延迟都相同,则所有这些回调将大致同时发生。尝试随机化示例中的延迟,看看它是否使用更少的线程。 -
您是否将此作为错误报告给 Mono 项目?我强烈反对糟糕的概念。 “可能创建线程”是一个错误的措辞,总是有一个 I/O 完成线程,它在硬件中断后运行代码。在使用 node.js 的情况下,它只会将完成回调编组到节点的主线程,在那里它会被事件循环拾取。您可以使用 .NET 使用自定义任务调度程序轻松实现此模型:stackoverflow.com/q/20993007/1768303
标签: c# multithreading mono .net-4.5 async-await