首页 > 解决方案 > 异步任务减慢代码(散列)

问题描述

我正在编写一个在单词列表上执行散列的 Windows 窗体应用程序。为了确保在散列进行时应用程序不会冻结,我正在使用异步任务来执行散列。但是,这样做会导致处理哈希的速度从每秒数千个下降到每秒大约 60 个。

我的哈希函数是这样的

private static string MD5Hash(string word)
{
    var stringBuilder = new StringBuilder();
    var MD5 = new MD5CryptoServiceProvider();
    var bytes = MD5.ComputeHash(new UTF8Encoding().GetBytes(word));

    foreach (var value in bytes)
    {
        stringBuilder.Append(value.ToString("X2"));
    }
    return stringBuilder.ToString();
}

我实现了散列函数来从这样的文件中散列单词(这没有使用异步,它每秒可以实现几千个散列)

private static void DoHashes()
{
    foreach (var word in File.ReadLines("the file path"))
    { 
        File.AppendAllText("the output path",  MD5Hash(word) + Environment.NewLine);         
    }   
}

然后我使用了一个异步任务来避免像这样冻结我的 Windows 窗体应用程序(这导致哈希的速度下降到每秒 60 左右)

private static async void DoHashes()
{
    await Task.Run(() =>
    {       
        foreach (var word in File.ReadLines("the file path"))
        { 
            File.AppendAllText("the output path",  MD5Hash(word) + Environment.NewLine);      
        }  
    });
}

如何避免这种速度下降,而不会导致我的 Windows 窗体在执行哈希时冻结?

标签: c#asynchronoushash

解决方案


我已经使用 WPF 应用程序完成了这个测试。在我的测试环境中,我使用了一个包含 5000 行的文件。以下是回复

|-------------------------------------------------------------------------------------
|#        Description                                            Time Taken (in secs)
|-------------------------------------------------------------------------------------
|1    Without Async/Await (As mentioned in the question)            144.933
|2    With Async/Await (As mentioned in the question)               145.563
|3    Using StringBuilder and writing to file only once             0.143
|4    With Async/Await and set ConfigureAwait to false              90.657
|-------------------------------------------------------------------------------------

如果您看到结果,则 Test#1 和 Test#2 之间没有重大区别,因此包装到 async-await 不会对您的方案产生影响。

下面是测试#3 和#4 的代码

测试 3(使用 StringBuilder 并仅写入文件一次)

private static async void DoHashes()
{
    Stopwatch sw = new Stopwatch();
    sw.Start();
    await Task.Run(() =>
    {
        StringBuilder sb = new StringBuilder();
        foreach (var word in File.ReadLines(Input file path))
        {
           sb.AppendLine(MD5Hash(word));
        }

       File.AppendAllText(Output file path, sb.ToString());
    });

    sw.Stop();

    MessageBox.Show("Time Taken by Do Hashes : " + (sw.ElapsedMilliseconds / 1000.0) + " secs");
}

该测试的结果是0.143(比测试#1 和 #2 好 1000 倍),因为进程没有多次获取文件句柄。

测试 4(使用 Async/Await 并将 ConfigureAwait 设置为 false)

private static async void DoHashes()
{
    Stopwatch sw = new Stopwatch();
    sw.Start();
    await Task.Run(() =>
    {
        foreach (var word in File.ReadLines(Input file path)
        {
           File.AppendAllText(Output file path, MD5Hash(word) + Environment.NewLine);
        }
    }).ConfigureAwait(false);

    sw.Stop();

    MessageBox.Show("Time Taken by Do Hashes : " + (sw.ElapsedMilliseconds / 1000.0) + " secs");
}

ConfigureAwait尝试设置为将继续编组回捕获的原始上下文,这样您就可以看到性能有所提高 --- 与 Test#1 和 Test#2 相比,它需要的时间减少了40%(仅需要 90.657 秒)。


推荐阅读