c# - 为什么在线程中运行异步操作比纯任务或纯线程操作慢得多

标签 c# multithreading performance asynchronous async-await

在调查一些性能问题时,我们偶然发现了一些我们不知道原因的结果

我们尝试运行具有不同循环计数和延迟的异步操作循环,并且我们有 3 种不同的构造,其中当线程数量增加时情况 2 运行得更慢(我们永远不会实际使用情况 2 中的代码,但是下面列出的结果的解释是什么):

案例一:

for (int i = 0; i < count; i++)
{
    tasks.Add(Task.Delay(delay));
}
Task.WaitAll(tasks.ToArray());

案例二:

for (int i = 0; i < count; i++)
{
    var task = Task.Run(() => { Task.Delay(delay).Wait(); });
    tasks.Add(task);
}
Task.WaitAll(tasks.ToArray());

案例三:

for (int i = 0; i < count; i++)
{
    tasks.Add(Task.Run(() => Thread.Sleep(delay)));
}
Task.WaitAll(tasks.ToArray());

为什么情况 2 与其他情况相比如此慢 - 请参阅下面的输出和完整程序?

RunAsync, count = 1, delay = 50
Async execution 1 times with Task.Delay of 50 ms. took 00:00:00.0510000, average = 00:00:00.0510000.
RunAsync, count = 5, delay = 50
Async execution 5 times with Task.Delay of 50 ms. took 00:00:00.0620000, average = 00:00:00.0124000.
RunAsync, count = 10, delay = 50
Async execution 10 times with Task.Delay of 50 ms. took 00:00:00.0660000, average = 00:00:00.0066000.
RunAsync, count = 50, delay = 50
Async execution 50 times with Task.Delay of 50 ms. took 00:00:00.0590000, average = 00:00:00.0011800.
RunAsync, count = 100, delay = 50
Async execution 100 times with Task.Delay of 50 ms. took 00:00:00.0620000, average = 00:00:00.0006200.
==================================================
RunAsyncInThread, count = 1, delay = 50
Task.Run 1 times with Task.Delay of 50 ms. took 00:00:00.0630000, average = 00:00:00.0630000.
RunAsyncInThread, count = 5, delay = 50
Task.Run 5 times with Task.Delay of 50 ms. took 00:00:00.0620000, average = 00:00:00.0124000.
RunAsyncInThread, count = 10, delay = 50
Task.Run 10 times with Task.Delay of 50 ms. took 00:00:00.7200000, average = 00:00:00.0720000.
RunAsyncInThread, count = 50, delay = 50

WHY ARE THESE SO SLOW:

Task.Run 50 times with Task.Delay of 50 ms. took 00:00:15.8100000, average = 00:00:00.3162000.
RunAsyncInThread, count = 100, delay = 50
Task.Run 100 times with Task.Delay of 50 ms. took 00:00:34.0600000, average = 00:00:00.3406000.
==================================================
RunThread, count = 1, delay = 50
Thread execution 1 times with Task.Delay of 50 ms. took 00:00:00.0500000, average = 00:00:00.0500000.
RunThread, count = 5, delay = 50
Thread execution 5 times with Task.Delay of 50 ms. took 00:00:00.0500000, average = 00:00:00.0100000.
RunThread, count = 10, delay = 50
Thread execution 10 times with Task.Delay of 50 ms. took 00:00:00.0500000, average = 00:00:00.0050000.
RunThread, count = 50, delay = 50
Thread execution 50 times with Task.Delay of 50 ms. took 00:00:00.0500000, average = 00:00:00.0010000.
RunThread, count = 100, delay = 50
Thread execution 100 times with Task.Delay of 50 ms. took 00:00:00.1000000, average = 00:00:00.0010000.

完整的测试程序在这里:

using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;

namespace ConsoleApplication2
{
    class Program
    {
        static void Main(string[] args)
        {
            RunAsync(1, 50);
            RunAsync(5, 50);
            RunAsync(10, 50);
            RunAsync(50, 50);
            RunAsync(100, 50);
            Console.WriteLine("==================================================");
            RunAsyncInThread(1, 50);
            RunAsyncInThread(5,50);
            RunAsyncInThread(10,50);
            RunAsyncInThread(50,50);
            RunAsyncInThread(100,50);
            Console.WriteLine("==================================================");
            RunThread(1, 50);
            RunThread(5,50);
            RunThread(10,50);
            RunThread(50,50);
            RunThread(100,50);

            //RunAsyncInThread(20, 50);
            //RunAsyncInThread(40, 50);
            //RunAsyncInThread(80, 50);
            //RunAsyncInThread(160, 50);
            //RunAsyncInThread(320, 50);
            Console.WriteLine("Press enter:");
            Console.ReadLine();
        }

        private static void RunAsyncInThread(int count, int delay)
        {
            Console.WriteLine("RunAsyncInThread, count = {0}, delay = {1} ", count, delay);
            var now = DateTime.UtcNow;
            var tasks = new List<Task>();
            for (int i = 0; i < count; i++)
            {
                var task = Task.Run(() => { Task.Delay(delay).Wait(); });
                tasks.Add(task);
            }
            Task.WaitAll(tasks.ToArray());
            var elapsed = DateTime.UtcNow - now;
            Console.WriteLine("Task.Run {0} times with Task.Delay of {1} ms. took {2}, average = {3}. ", count, delay, elapsed, TimeSpan.FromTicks(elapsed.Ticks / count));
        }

        private static void RunAsync(int count, int delay)
        {
            Console.WriteLine("RunAsync, count = {0}, delay = {1} ",count,delay);
            var now = DateTime.UtcNow;
            var tasks = new List<Task>();
            for (int i = 0; i < count; i++)
            {
                tasks.Add(Task.Delay(delay));
            }
            Task.WaitAll(tasks.ToArray());
            var elapsed = DateTime.UtcNow - now;
            Console.WriteLine("Async execution {0} times with Task.Delay of {1} ms. took {2}, average = {3}. ", count, delay, elapsed, TimeSpan.FromTicks(elapsed.Ticks / count));
        }

        private static void RunThread(int count, int delay)
        {
            Console.WriteLine("RunThread, count = {0}, delay = {1} ",count,delay);
            var now = DateTime.UtcNow;
            var tasks = new List<Task>();
            for (int i = 0; i < count; i++)
            {
                tasks.Add(Task.Run(() => Thread.Sleep(delay)));
            }
            Task.WaitAll(tasks.ToArray());
            var elapsed = DateTime.UtcNow - now;
            Console.WriteLine("Thread execution {0} times with Task.Delay of {1} ms. took {2}, average = {3}. ", count, delay, elapsed, TimeSpan.FromTicks(elapsed.Ticks / count));
        }

    }
}

最佳答案

为什么调度线程池线程启动异步操作,然后坐在那里等待异步操作完成,而不是直接执行异步操作会比较慢?

您节省了等待线程池中安排操作的开销,如果线程池饱和,比如说因为您突然用一百个请求淹没它,这可能需要一些时间。安排好操作后,它最终可以开始执行异步操作。

为什么创建一个全新的线程会比较慢,这样您就可以开始一个异步操作然后等待它完成而不是仅仅开始一个异步操作?

因为您需要花费所有的时间和精力来创建一个全新的线程,并等待它第一次被调度,然后您才能开始您的异步操作。 p>

关于c# - 为什么在线程中运行异步操作比纯任务或纯线程操作慢得多,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27429864/

相关文章:

c# - ASP.NET MVC : seperatation of concern for normal/admin user

c# - 创建新的设置实例

java - 关于 Java 发生前关系,需要说明

visual-studio-2010 - 在 VS2010 上构建解决方案时如何找到 "slow"位置?

python - 有效地 reshape pandas 数据框列中的数组

c# - 如何在 MVC5 中手动检查 url 授权?

java - 执行器服务和scheduleWithFixedDelay()

c# - 如何在运行时控制线程的CPU利用率?

java - 为什么检查 HashMap 是否具有特定值需要很长时间才能在 for 循环中执行?

c# - 使用 C# 快速获取 Active Directory 中组成员列表的方法