【问题标题】:How to measure performance of awaiting asynchronous operations?如何衡量等待异步操作的性能?
【发布时间】:2014-06-15 02:32:16
【问题描述】:

我有一个从多个 MessageQueue 实例读取的 Windows 服务。这些消息队列都运行自己的Task 来读取消息。通常,在阅读完一条消息后,I/O 数据库的工作就完成了。我发现一些文章声称在 I/O 操作上使用异步是个好主意,因为它会释放线程。我正在尝试模拟在控制台应用程序中使用异步 I/O 操作的性能提升。

控制台应用程序

在我的测试环境中,我有 10 个队列。 GetQueues() 返回 10 个不同的 MessageQueue 实例。

static void Main(string[] args)
{
    var isAsync = Console.ReadLine() == "Y";
    foreach (var queue in queueManager.GetQueues())
    {
        var temp = queue;
        Task.Run(() => ReceiveMessagesForQueue(temp, isAsync));
    }

    while (true)
    {
        FillAllQueuesWithMessages();
        ResetAndStartStopWatch();
        while(!AllMessagesRead())
        {
            Thread.Sleep(10);
        }
        Console.WriteLine("All messages read in {0}ms", stopWatch.ElapsedMilliseconds);
    }
}

static async Task ReceiveMessagesForQueue(MessageQueue queue, bool isAsync)
{
    while (true)
    {
        var message = await Task.Factory.FromAsync<Message>(queue.BeginReceive(), queue.EndReceive);

        if (isAsync)
            await ProcessMessageAsync(message);
        else
            ProcessMessage(message);
    }
}

异步消息处理

Task.Delay() 上使用 await,所以应该释放当前的 Thread

static async Task ProcessMessageAsync(Message message)
{
    await Task.Delay(1000);
    BurnCpu();
}

同步消息处理

等待Task.Delay(),所以不应该释放当前的Thread

static void ProcessMessage(Message message)
{
    Task.Delay(1000).Wait();
    BurnCpu();
}

最终,结果是平等的。我在这里遗漏了什么吗?

编辑 1

我正在使用stopWatch.ElapsedMilliseconds 测量总时间。我使用 FillAllQueuesWithMessages() 填充所有队列,其中包含 10、100、10000 条或更多消息。

编辑 2

ReceiveMessagesForQueue() 现在返回 Task 而不是 void

编辑 3(修复)

这个测试确实显示了我现在的性能改进。我不得不让BurnCpu() 花费更多时间。在等待Task.Delay() 时,BurnCPU() 可以使用释放的线程进行处理。

【问题讨论】:

    标签: c# task-parallel-library async-await performance-testing c#-5.0


    【解决方案1】:

    使用async-await 不会加快执行单个操作所需的时间,它只是意味着您没有等待什么都不做的线程

    在您的情况下,Task.Delay 无论如何都需要一秒钟,但在这里:

    Task.Delay(1000).Wait();
    

    一个线程在这里等待第二个结束:

    await Task.Delay(1000);
    

    。您仍在异步等待(因此,await)但没有使用线程,这意味着更好的可伸缩性。

    async-await 中,您可以获得性能提升,因为您的应用可以用更少的线程做同样的事情,或者用同样的线程做更多的事情。要衡量您需要同时进行大量async 操作。只有这样您才会注意到async 选项比同步选项更好地利用CPU 资源。


    更多关于释放线程的信息在这里There Is No Thread

    【讨论】:

    • 我知道我正在释放线程。以下是我无法解决的问题:如果我有一个 4 核处理器运行 10 个并行进程从队列中读取消息,那么在 Task.Delay() 上使用 await 时不应该是线程切换吗?我想使用已发布的线程。如果我为 10 个进程中的 1 个释放线程,为什么其他线程不使用该线程?
    • @annemartijn 当您说进程时,您是指线程吗?要从使用 async-await 中获得改进,您需要让 CPU 工作和 IO(或其他异步)同时工作(在您的应用程序中)。这样,当一个线程在 IO 上被“阻塞”时,它就可以去做其他 CPU 工作,而不是无所事事地等待。
    • @annemartijn 你没有错。使用“无线程异步”允许您同时执行更多 IO(或异步)调用,因为它不会耗尽资源,并且线程可以同时执行 CPU 工作。这就是可扩展性的含义。
    • @annemartijn 事实并非如此。当线程被阻塞时(例如 Thread.Sleep),CPU 将其切断并进行上下文切换。此外,您有不止 4 个线程,如果需要,线程池可以创建更多。
    • 是的。 Async 不会缩短您的异步操作。他们需要尽可能长的时间。但它释放了线程,因此您可以触发更多异步操作或使用 CPU 时间。
    【解决方案2】:

    您仍然在线程池中的自己的线程中运行每个任务 - 因为您使用的是默认任务调度程序。如果您想看到性能提升,您需要确保在同一个线程上执行多个任务。

    此外,如果有 20 个并行任务,您可能不会看到任何差异。尝试 2,000 个任务。

    【讨论】:

      猜你喜欢
      • 2014-07-15
      • 1970-01-01
      • 2023-03-27
      • 1970-01-01
      • 2015-01-31
      • 1970-01-01
      • 1970-01-01
      • 2016-12-26
      • 1970-01-01
      相关资源
      最近更新 更多