Winforms 递归文件扫描块 UI
Winforms recursive file scan blocks UI
我有一个程序可以搜索给定目录并将所有文件添加到列表视图中。我的问题是 ui 线程在搜索繁忙时卡住了。我试过使用任务,但无法让它异步工作。找到每个文件后必须更新列表视图。
我已经阅读了大量有关 TPL 及其使用方法的资料,但在这种情况下无法正常工作。我让它工作,其中数据处理是在一种创建任务来处理它的方法中进行的。谁能告诉我下面的代码有什么问题以及如何解决?
这是我的代码:
private void button1_Click(object sender, EventArgs e)
{
Task.Run(() =>
{
WalkDirectory(new DirectoryInfo(drive));
});
}
public void testTaskUpdateLabel(string labelTeks)
{
Task taskUpdateLabel = new Task(() =>
{
label4.Text = labelTeks;
});
taskUpdateLabel.Start(uiScheduler);
}
public void testTaskUpdateLabel(string labelTeks)
{
Task taskUpdateLabel = new Task(() =>
{
label4.Text = labelTeks;
});
taskUpdateLabel.Start(uiScheduler);
}
public bool WalkDirectory(DirectoryInfo directory)
{
if (directory == null)
{
throw new ArgumentNullException("directory");
}
return this.WalkDirectories(directory);
}
private bool WalkDirectories(DirectoryInfo directory)
{
bool continueScan = true;
continueScan = WalkFilesInDirectory(directory);
if (continueScan)
{
DirectoryInfo[] subDirectories = directory.GetDirectories();
foreach (DirectoryInfo subDirectory in subDirectories)
{
try
{
if ((subDirectory.Attributes & FileAttributes.ReparsePoint) != 0)
{
continue;
}
if (!(continueScan = WalkDirectory(subDirectory)))
{
break;
}
}
catch (UnauthorizedAccessException)
{
continue;
}
}
}
if (continueScan)
{
testTaskUpdateLabel(directory.FullName);
}
return continueScan;
}
private bool WalkFilesInDirectory(DirectoryInfo directory)
{
bool continueScan = true;
// Break up the search pattern in separate patterns
string[] searchPatterns = _searchPattern.Split(';');
// Try to find files for each search pattern
foreach (string searchPattern in searchPatterns)
{
if (!continueScan)
{
break;
}
// Scan all files in the current path
foreach (FileInfo file in directory.GetFiles(searchPattern))
{
try
{
testTaskUpdate(file.FullName);
}
catch (UnauthorizedAccessException)
{
continue;
}
}
}
return continueScan;
}
如果您使用 BackgroundWorker class,UI 将起作用并且可以在 ProgressChanged 事件处理程序中更新进度。
Can any one tel me what is wrong in the code below and how to fix it?
问题就在这里
public void testTaskUpdateLabel(string labelTeks)
{
Task taskUpdateLabel = new Task(() =>
{
label4.Text = labelTeks;
});
taskUpdateLabel.Start(uiScheduler);
}
您不应使用 TPL 更新 UI。 TPL 任务用于执行非 UI 工作,并且 UI 应该只在 UI 线程上更新。您已经将工作转移到线程池线程上(通过 Task.Run
),因此您需要解决的唯一问题是如何从工作线程内部更新 UI。有很多方法可以做到这一点——使用 Control.Invoke/BeginInvoke
、SynchronizationContext
等,但 TPL 的首选方法是传递并使用 IProgress<T> interface. Don't be fooled by the name - the interface is an abstraction of a callback with some data. There is a standard BCL provided implementation - Progress<T> class 并具有以下行为,根据文档
Any handler provided to the constructor or event handlers registered with the ProgressChanged event are invoked through a SynchronizationContext instance captured when the instance is constructed.
即非常适合 UI 更新场景。
综上所述,以下是如何将其应用到您的代码中。我们将使用 IProgress<string>
并将调用 Report
方法并传递我们找到的每个 file/directory 的全名 - 直接替换您的 testTaskUpdateLabel
调用。
private void button1_Click(object sender, EventArgs e)
{
var progress = new Progress<string>(text => label4.Text = text);
Task.Run(() =>
{
WalkDirectory(new DirectoryInfo(drive), progress);
});
}
public bool WalkDirectory(DirectoryInfo directory, IProgress<string> progress)
{
if (directory == null) throw new ArgumentNullException("directory");
if (progress == null) throw new ArgumentNullException("progress");
return WalkDirectories(directory, progress);
}
bool WalkDirectories(DirectoryInfo directory, IProgress<string> progress)
{
// ...
if (!(continueScan = WalkDirectories(subDirectory, progress)))
// ...
if (continueScan)
progress.Report(directory.FullName);
// ...
}
bool WalkFilesInDirectory(DirectoryInfo directory, IProgress<string> progress)
{
// ...
try
{
progress.Report(file.FullName);
}
// ...
}
我通过使 walkDirectory、walkDirectories 和 WalkFiles 方法异步来让它工作。因此在我调用 testUpdate 和 testUpdateLabel 方法之前使用 await 关键字。这样,当搜索 运行 时,列表视图会随着搜索结果更新,而不会阻塞 UI 线程。 IE。用户可以在找到他要搜索的文件后取消搜索。
我有一个程序可以搜索给定目录并将所有文件添加到列表视图中。我的问题是 ui 线程在搜索繁忙时卡住了。我试过使用任务,但无法让它异步工作。找到每个文件后必须更新列表视图。
我已经阅读了大量有关 TPL 及其使用方法的资料,但在这种情况下无法正常工作。我让它工作,其中数据处理是在一种创建任务来处理它的方法中进行的。谁能告诉我下面的代码有什么问题以及如何解决?
这是我的代码:
private void button1_Click(object sender, EventArgs e)
{
Task.Run(() =>
{
WalkDirectory(new DirectoryInfo(drive));
});
}
public void testTaskUpdateLabel(string labelTeks)
{
Task taskUpdateLabel = new Task(() =>
{
label4.Text = labelTeks;
});
taskUpdateLabel.Start(uiScheduler);
}
public void testTaskUpdateLabel(string labelTeks)
{
Task taskUpdateLabel = new Task(() =>
{
label4.Text = labelTeks;
});
taskUpdateLabel.Start(uiScheduler);
}
public bool WalkDirectory(DirectoryInfo directory)
{
if (directory == null)
{
throw new ArgumentNullException("directory");
}
return this.WalkDirectories(directory);
}
private bool WalkDirectories(DirectoryInfo directory)
{
bool continueScan = true;
continueScan = WalkFilesInDirectory(directory);
if (continueScan)
{
DirectoryInfo[] subDirectories = directory.GetDirectories();
foreach (DirectoryInfo subDirectory in subDirectories)
{
try
{
if ((subDirectory.Attributes & FileAttributes.ReparsePoint) != 0)
{
continue;
}
if (!(continueScan = WalkDirectory(subDirectory)))
{
break;
}
}
catch (UnauthorizedAccessException)
{
continue;
}
}
}
if (continueScan)
{
testTaskUpdateLabel(directory.FullName);
}
return continueScan;
}
private bool WalkFilesInDirectory(DirectoryInfo directory)
{
bool continueScan = true;
// Break up the search pattern in separate patterns
string[] searchPatterns = _searchPattern.Split(';');
// Try to find files for each search pattern
foreach (string searchPattern in searchPatterns)
{
if (!continueScan)
{
break;
}
// Scan all files in the current path
foreach (FileInfo file in directory.GetFiles(searchPattern))
{
try
{
testTaskUpdate(file.FullName);
}
catch (UnauthorizedAccessException)
{
continue;
}
}
}
return continueScan;
}
如果您使用 BackgroundWorker class,UI 将起作用并且可以在 ProgressChanged 事件处理程序中更新进度。
Can any one tel me what is wrong in the code below and how to fix it?
问题就在这里
public void testTaskUpdateLabel(string labelTeks)
{
Task taskUpdateLabel = new Task(() =>
{
label4.Text = labelTeks;
});
taskUpdateLabel.Start(uiScheduler);
}
您不应使用 TPL 更新 UI。 TPL 任务用于执行非 UI 工作,并且 UI 应该只在 UI 线程上更新。您已经将工作转移到线程池线程上(通过 Task.Run
),因此您需要解决的唯一问题是如何从工作线程内部更新 UI。有很多方法可以做到这一点——使用 Control.Invoke/BeginInvoke
、SynchronizationContext
等,但 TPL 的首选方法是传递并使用 IProgress<T> interface. Don't be fooled by the name - the interface is an abstraction of a callback with some data. There is a standard BCL provided implementation - Progress<T> class 并具有以下行为,根据文档
Any handler provided to the constructor or event handlers registered with the ProgressChanged event are invoked through a SynchronizationContext instance captured when the instance is constructed.
即非常适合 UI 更新场景。
综上所述,以下是如何将其应用到您的代码中。我们将使用 IProgress<string>
并将调用 Report
方法并传递我们找到的每个 file/directory 的全名 - 直接替换您的 testTaskUpdateLabel
调用。
private void button1_Click(object sender, EventArgs e)
{
var progress = new Progress<string>(text => label4.Text = text);
Task.Run(() =>
{
WalkDirectory(new DirectoryInfo(drive), progress);
});
}
public bool WalkDirectory(DirectoryInfo directory, IProgress<string> progress)
{
if (directory == null) throw new ArgumentNullException("directory");
if (progress == null) throw new ArgumentNullException("progress");
return WalkDirectories(directory, progress);
}
bool WalkDirectories(DirectoryInfo directory, IProgress<string> progress)
{
// ...
if (!(continueScan = WalkDirectories(subDirectory, progress)))
// ...
if (continueScan)
progress.Report(directory.FullName);
// ...
}
bool WalkFilesInDirectory(DirectoryInfo directory, IProgress<string> progress)
{
// ...
try
{
progress.Report(file.FullName);
}
// ...
}
我通过使 walkDirectory、walkDirectories 和 WalkFiles 方法异步来让它工作。因此在我调用 testUpdate 和 testUpdateLabel 方法之前使用 await 关键字。这样,当搜索 运行 时,列表视图会随着搜索结果更新,而不会阻塞 UI 线程。 IE。用户可以在找到他要搜索的文件后取消搜索。