繁体   English   中英

C#中的线程和递归

[英]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);
}

在异步情况下,您需要在主线程填充列表视图之前等待后台线程填充列表。 使用任务以及asyncawait关键字非常简单。

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-WinFormsRx-WPF即可上手。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM