首页 > 解决方案 > 为什么这个 MVC 方法不能以并行方式运行?

问题描述

背景

我有一个 MVC 5 应用程序,想测试请求是否并行运行。为此,我使用了下面的代码,并打开了多个发出相同请求的页面。

代码

下面是一个相对简单的方法,我想要并行性。

public async Task<ActionResult> Login(string returnUrl, string message = "")
{
    var rng = new Random();

    var wait = rng.Next(3, 10);

    var threadGuid = Guid.NewGuid();
    DebugHelper.WriteToDebugLog($"Thread {threadGuid} about to wait {wait} seconds");
    await Task.Delay(wait * 1000);
    DebugHelper.WriteToDebugLog($"Thread {threadGuid} finished");

    return View();
}

该类DebugHelper仅用于我可以安全地写入文件。

public static class DebugHelper
{
    private static readonly object WriteLock = new object();

    public static void WriteToDebugLog(string message, string path = "C:\\Temp\\Log.txt")
    {
        lock (WriteLock)
        {
            File.AppendAllLines(path, new string[] { "", GetDateString(), message });
        }
    }
}

输出

我一直得到这种类型的输出,这表明线程相互阻塞。

2020-03-24T13:43:43.1431913Z
Thread 6e42a6c5-d3cb-4541-b8aa-34b290952973 about to wait 7 seconds

2020-03-24T13:43:50.1564077Z
Thread 6e42a6c5-d3cb-4541-b8aa-34b290952973 finished

2020-03-24T13:43:50.1853278Z
Thread 90923f55-befd-4224-bdd8-b67f787839fc about to wait 4 seconds

2020-03-24T13:43:54.1943271Z
Thread 90923f55-befd-4224-bdd8-b67f787839fc finished

2020-03-24T13:43:54.2312257Z
Thread fa2d8d30-b762-4262-b188-0b34da5f4f04 about to wait 3 seconds

2020-03-24T13:43:57.2370556Z
Thread fa2d8d30-b762-4262-b188-0b34da5f4f04 finished

2020-03-24T13:43:57.2679690Z
Thread 37311a0e-d19e-4563-b92a-5e5e3def379a about to wait 8 seconds

2020-03-24T13:44:05.2812367Z
Thread 37311a0e-d19e-4563-b92a-5e5e3def379a finished

问题

为什么会出现这种情况?

我的印象是任何 ASP.NET 应用程序一开始都是多线程的,所以即使在我没有async/await设置的情况下,我认为它会同时运行这些线程。

更新

正如答案/评论中所建议的,我的方法是错误的。使用以下代码后,我可以在日志中非常清楚地看到它确实在并行运行。

var targetTime = DateTime.UtcNow + TimeSpan.FromSeconds(5);
while(DateTime.UtcNow < targetTime)
{
    DebugHelper.WriteToDebugLog($"Thread {threadGuid} with ID {threadId} doing stuff");
    await Task.Delay(1000);
}

标签: c#asp.netasp.net-mvcmultithreadingtask

解决方案


它简单地归结为这样一个事实,即您的调试日志记录及其WriteLock同步 File.AppendAllLines强制同步锁定到所有调用它的异步函数上。

您最好有一个异步写入到调试过程,这将允许您的任务继续运行。

产品/消费者模式、信号量、事件、异步文件访问 API 的使用都浮现在脑海中。


推荐阅读