我遇到一个奇怪的问题, 应该工作(据我所知),但事实并非如此。
我有一个Windows窗体应用程序。它有一个按钮,可以扫描选定的目录,这可能会导致找到大量文件。扫描~50k文件时,大约需要10秒钟。
我尝试实现异步,将文件扫描本身异步运行到主GUI,但这会造成麻烦。任务本身运行正常,但它仍然阻止了GUI,导致整个应用程序冻结。
这是按钮的代码
private async void BeginScanButton_Click(object sender, EventArgs e)
{
if (_osuDirectory == null)
MessageBox.Show("You have not chosen an Osu! directory yet.");
else
{
await ScanFilesTask();
}
还有很多,但这是它的相关部分。
任务本身是
private Task ScanFilesTask()
{
FileList.Clear();
return Task.Run(() =>
{
if (_jpgFilesChecked)
FileParser.ParseFiles(_osuDirectory, "*.jpg");
if (_pngFilesChecked)
FileParser.ParseFiles(_osuDirectory, "*.png");
if (_wavFilesChecked)
FileParser.ParseFiles(_osuDirectory, "*.wav");
if (_aviFilesChecked)
FileParser.ParseFiles(_osuDirectory, ".avi");
});
}
最后,FileParser类只是
public static void ParseFiles(string dir, string extension)
{
Form1.FileList.AddRange(Directory.GetFiles(dir, extension, SearchOption.AllDirectories));
}
(如果有人想查看,所有代码都在github repo上。相关的异步代码在AsyncTest分支中。)
非常感谢任何帮助。这是我的第一个真正的C#项目,非常感谢任何指向正确的方向。提前谢谢!
答案 0 :(得分:1)
要阅读的内容:
Task.Run Etiquette and Proper Usage
Task.Run vs BackgroundWorker, Round 1: The Basic Pattern
A Tour of Task, Part 0: Overview
摘录:
自.NET 4.0中引入以来使用过Task和TPL(任务并行库)的开发人员。这些开发人员熟悉Task以及如何在并行处理中使用它。这些开发人员面临的危险是Task(因为它被TPL使用)与Task完全不同(因为它被async使用)。
在异步出现之前从未听说过Task的开发人员。对他们来说,Task只是异步的一部分 - 还有一个(相当复杂的)要学习的东西。 “延续”是一个外来词。这些开发人员面临的危险是假设Task的每个成员都适用于异步编程,但肯定不是这样。
Async中没有意义,你想要的是并行运行。他们是非常不同的。您正在寻找的是:
按钮:
private void BeginScanButton_Click(object sender, EventArgs e)
{
BeginScanButton.Enabled = false;
if (_osuDirectory == null)
MessageBox.Show("You have not chosen an Osu! directory yet.");
BeginScanButton.Enabled = false;
else
{
ScanFilesInParallel();
}
}
ScanFiles
private void ScanFilesInParallel()
{
FileList.Clear();
Task.Run(() =>
{
var result = new List<string>();
if (_jpgFilesChecked)
result.AddRange(FileParser.ParseFiles(_osuDirectory, "*.jpg"));
if (_pngFilesChecked)
result.AddRange(FileParser.ParseFiles(_osuDirectory, "*.png"));
if (_wavFilesChecked)
result.AddRange(FileParser.ParseFiles(_osuDirectory, "*.wav"));
if (_aviFilesChecked)
result.AddRange(FileParser.ParseFiles(_osuDirectory, ".avi"));
return result;
})
.ContinueWith((task) => {
FileList.AddRange(task.Result);
BeginScanButton.Enabled = true;
}, TaskScheduler.FromCurrentSynchronizationContext());
}
ParseFiles
public IEnumerable<string> ParseFiles(string dir, string extension)
{
var result = Directory.GetFiles(dir, extension, SearchOption.AllDirectories).ToList();
return result;
}
答案 1 :(得分:0)
只是给你一个我认为读得更好的选择;尝试使用Microsoft的Reactive Framework。
然后你可以这样做:
private async void BeginScanButton_Click(object sender, EventArgs e)
{
if (_osuDirectory == null)
MessageBox.Show("You have not chosen an Osu! directory yet.");
else
{
var exts = new[]
{
_jpgFilesChecked ? "*.jpg" : null,
_pngFilesChecked ? "*.png" : null,
_wavFilesChecked ? "*.wav" : null,
_aviFilesChecked ? "*.avi" : null,
}.Where(x => x != null);
var query =
from ext in exts.ToObservable()
from fs in Observable.Start(() => FileParser.ParseFiles(_osuDirectory, ext))
from f in fs
select f;
var files = await query.ToArray();
FileList.AddRange(files);
}
}
public static class FileParser
{
public static string[] ParseFiles(string dir, string extension)
{
return Directory.GetFiles(dir, extension, SearchOption.AllDirectories);
}
}
它们全部并行处理,并在UI线程上完成。
NuGet&#34; System.Reactive&#34;,&#34; System.Reactive.Windows.Forms&#34;和/或&#34; System.Reactive.Windows.Threading&#34; (WPF)得到这些位。