Task.Delay 是否会启动一个新线程? [英] Does Task.Delay start a new thread?

查看:44
本文介绍了Task.Delay 是否会启动一个新线程?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

以下代码应该(至少在我看来)创建 100 个 Tasks,它们都在并行等待(这就是并发性的重点,对 :D ?)并且几乎同时完成.我猜对于每个 Task.Delay 一个 Timer 对象是在内部创建的.

The following code should (at least in my opinion) create 100 Tasks, which are all waiting in parallel (that's the point about concurrency, right :D ?) and finish almost at the same time. I guess for every Task.Delay a Timerobject is created internally.

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 上运行它时,我得到了非常奇怪的行为:

But! When I run this on Mono I get very strange behavior:

  • Tasks 不会同时完成,会有很大的延迟(大概 500-600 毫秒)
  • 在控制台中,mono 显示了很多创建的线程:
  • The Tasks do not finish at the same time, there are huge delays (probably about 500-600ms)
  • In the console mono shows a lot of created threads:

加载的程序集:/Users/xxxxx/Programming/xxxxx/xxxxxxxxxx/bin/Release/xxxxx.exe

Loaded assembly: /Users/xxxxx/Programming/xxxxx/xxxxxxxxxx/bin/Release/xxxxx.exe

线程开始:#2

线程开始:#3

线程开始:#4

线程开始:#5

线程开始:#6

线程开始:#7

线程完成:#3 <-- 显然1000ms的延迟完成了?

Thread finished: #3 <-- Obviously the delay of 1000ms finished ?

线程完成:#2 <-- 显然1000ms的延迟完成了?

Thread finished: #2 <-- Obviously the delay of 1000ms finished ?

线程开始:#8

线程开始:#9

线程开始:#10

线程开始:#11

线程开始:#12

线程开始:#13

……你懂的.

这真的是一个错误吗?还是我用错了图书馆?

Is this actually a bug ? Or do I use the library wrong ?

我使用 Timer 测试了自定义睡眠方法:

I tested a custom sleep method using 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;
    }

这一次,所有任务瞬间完成.所以,我认为这是一个非常糟糕的实现或错误.

This time, all tasks completed instantaneously. So, I think it's a really bad implementation or a bug.

仅供参考:我现在使用 Windows 8.1 在 .NET 上测试了原始代码(使用 Task.Delay),它按预期运行(1000 个 Tasks,等待 1 秒并行完成).

Just FYI: I've tested the original code (using Task.Delay) on .NET using Windows 8.1 now and it ran as expected (1000 Tasks, waiting for 1 second in parallel and finishing).

所以答案是:Mono 的实现.的(某些)方法并不完美.一般来说,Task.Delay 不会启动一个线程,甚至很多都不应该创建多个线程.

So the answer is: Mono's impl. of (some) methods is not perfect. In general Task.Delay does not start a thread and even a lot of them should not create multiple threads.

推荐答案

Task 库更多地设计用于在不阻塞整个工作流的情况下管理阻塞任务(任务异步,被混淆地称为任务并行")Microsoft),并且用于执行大型并发计算块(并行执行).

The Task library is designed more for managing blocking tasks without blocking an entire workflow (task asynchronism, confusingly called "task parallel" by Microsoft), and not for doing large blocks of concurrent computation (parallel execution).

任务库使用调度程序并将准备执行的作业排队.当作业运行时,它们将在线程池线程上运行,并且数量非常有限.增加线程数是有逻辑的,但除非您有数百个 CPU 内核,否则它将保持在较低的数量.

The task library uses a scheduler and queues jobs ready for execution. When jobs are run, they will do so on a thread-pool thread, and these are very limited in number. There is logic to expand the thread count, but unless you have hundreds of CPU cores, it's going to stay a low number.

所以为了回答这个问题,你的一些任务正在排队等待池中的线程,而其他延迟任务已经由调度程序发出.

So to answer the question, some of your tasks are queued up waiting for a thread from the pool, while the other delayed tasks have been issued by the scheduler.

调度程序和线程池逻辑可以在运行时更改,但是如果您想快速完成大量计算,Task 不适合这项工作.如果您想处理大量缓慢的资源(如磁盘、数据库或互联网资源),Task 可能有助于保持应用的响应速度.

The scheduler and thread-pool logic can be changed at runtime, but if you are trying to get lots of computation done quickly Task isn't right for the job. If you want to deal with lots of slow resources (like disk, database, or internet resources) Task may help keep an app responsive.

如果你只是想了解Task,试试这些:

If you just want to learn about Task try these:

这篇关于Task.Delay 是否会启动一个新线程?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆