我正在编写一个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();
}
我实现散列函数来散列像这样的文件中的单词(这不使用async,每秒可以实现几千个哈希值)
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);
}
});
}
如何在不执行哈希值的情况下使窗体形状冻结,以避免速度降低?
答案 0 :(得分:4)
我使用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
|-------------------------------------------------------------------------------------
如果您看到结果,则测试#1和测试#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
尝试设置为 NOT 以将延续编组回到捕获的原始上下文,这样您就可以看到性能提升了 - 与测试#1和测试#2相比它需要 40%更少的时间(仅需90.657秒)。
答案 1 :(得分:0)
根据你所说的你所做的一切都很慢。
采取这个基本案例:
var source = Enumerable.Range(0, 1000000).Select(x => x.ToString()).ToArray();
var sw = Stopwatch.StartNew();
var results = source.Select(x => MD5Hash(x)).ToArray();
sw.Stop();
Console.WriteLine(sw.Elapsed.TotalMilliseconds);
此代码在5316.8091
毫秒内完成。这是每毫秒188个哈希值。
如果我将测试放在async
方法中并运行:
var results = await Task.Run(() => source.Select(x => MD5Hash(x)).ToArray());
...然后每毫秒需要5531.4172
毫秒或大约181个哈希值。
没有async
我就跑了:
var results = Task.Run(() => source.Select(x => MD5Hash(x)).ToArray()).Result;
它以5441.0798
毫秒或大约184个每毫秒的哈希值完成。
因此,我只能得出结论,您提供的代码并不会导致速度减慢。
现在,如果您想尝试使用Microsoft的Reactive Framework,请尝试更快地运行。然后你可以这样写:
var sw = Stopwatch.StartNew();
var query =
from n in Observable.Range(0, 1000000)
from h in Observable.Start(() => MD5Hash(n.ToString()))
select new { n, h };
query
.ToArray()
.Subscribe(xs =>
{
var results = xs.OrderBy(x => x.n).Select(x => x.h).ToArray();
sw.Stop();
Console.WriteLine(sw.Elapsed.TotalMilliseconds);
});
在多个后台线程上运行,但可以编组回UI并在5秒内运行。
Just NuGet" System.Reactive"并将using System.Reactive.Linq
添加到您的代码中。
答案 2 :(得分:-2)
如果您在散列期间/之后不需要任何表单交互,则不要使用异步。任务就足够了。
private static void DoHashes()
{
Task.Run(() =>
{
foreach (var word in File.ReadLines("the file path"))
{
File.AppendAllText("the output path", MD5Hash(word) + Environment.NewLine);
}
});
}
另外,放置static
s