[英]Threading and Recursion in C#
我有此功能,它带有路径并在其中搜索文件。 我有两种方法:一种是使主线程完成任务,另一种是使工作线程完成任务。 当主线程执行作业时,它将返回所有文件,但是当工作线程执行作业时,将仅返回该路径中的少量文件。 它不执行输入子目录的递归步骤。 这是代码:
public void GetAllFiles(string sdir)
{
foreach (string dir in Directory.GetDirectories(sdir))
{
try
{
foreach (string file in Directory.GetFiles(dir, "*.*"))
{
string filename = Path.GetFileName(file);
listView1.Items.Add(filename);
}
GetAllFiles(dir);
}
catch (Exception error)
{
Console.WriteLine(error.Message);
}
}
}
这是我如何调用线程:
Thread thread = new Thread(() => GetAllFiles("C:\\Users\\modz\\Desktop\\games"));
thread.Start();
当您在后台线程上运行时,代码将失败,因为您试图从GUI线程以外的其他线程修改控件,这是不允许的。 要解决此问题,您必须将结果累积到后台线程中的某种集合中,然后从主线程中填充列表视图。
但是,有一个内置功能可以实现您想要的功能。 因此,最简单且可能最好的解决方案如下:
var files = Directory.GetFiles(path, "*", SearchOption.AllDirectories);
foreach (var file in files) {
listView1.Items.Add(file);
}
如果要手动执行此操作,则仍可以对方法进行一些修改,例如:
public void GetAllFiles(string sdir, List<string> files) {
foreach (string dir in Directory.GetDirectories(sdir)) {
try {
foreach (string file in Directory.GetFiles(dir, "*.*")) {
string filename = Path.GetFileName(file);
files.Add(filename);
}
GetAllFiles(dir, files);
} catch (Exception error) {
Console.WriteLine(error.Message);
}
}
}
然后,您可以像下面这样同步调用它:
var files = new List<string>();
GetAllFiles(path, files);
foreach (var file in files) {
listView1.Items.Add(file);
}
在异步情况下,您需要在主线程填充列表视图之前等待后台线程填充列表。 使用任务以及async
和await
关键字非常简单。
public async void Populate() {
const string path = ...
var files = new List<string>();
await Task.Run(() => GetAllFiles(path, files));
foreach (var file in files) {
listView1.Items.Add(file);
}
}
现有代码的问题是您正在从非UI线程更新UI元素。 您当然可以在后台线程上收集数据,但是在将数据添加到列表视图之前,必须将数据编组回UI。
为此,您应该简化生活,并使用避免需要滚动自己的线程的框架。
我建议使用TPL, await
/ async
或Microsoft的Reactive Framework。 我的选择是后者。
首先定义一个可观察的查询所有文件:
public IObservable<string> GetAllFiles(string sdir)
{
return
from dir in Directory.GetDirectories(sdir).ToObservable(Scheduler.Default)
from file in Directory.GetFiles(dir, "*.*").ToObservable(Scheduler.Default)
.Concat(GetAllFiles(dir))
select file;
}
请注意,此方法是递归的,并使用后台线程(通过Scheduler.Default
)来完成工作。
现在你需要消耗它
GetAllFiles("C:\\Users\\modz\\Desktop\\games")
.ObserveOn(listView1)
.Subscribe(filename => listView1.Items.Add(filename));
.ObserveOn(listView1)
所有编组回UI线程。 listView1
参数可以是任何UI窗体或控件。
.Subscribe(...)
仅获取所有文件名,然后将其添加到列表视图中。
此代码应该非常易于使用。 只需NuGet Rx-WinForms
或Rx-WPF
即可上手。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.