下面的代码应该(至少在我看来)创建 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 试试这些:

  • The Task library
  • The Scheduler
  • 关于c# - Task.Delay 是否启动一个新线程?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/21878050/

    10-15 17:06