【问题标题】:yield return IEnumerable in a background thread在后台线程中产生返回 IEnumerable
【发布时间】:2013-02-18 15:29:34
【问题描述】:

我有以下方法返回目录中的文件列表:

    public IEnumerable<FileInfo> GetFilesRecursiveEnumerable(DirectoryInfo dir)
    {
        if (dir == null) throw new ArgumentNullException();
        IList<FileSystemInfo> files = new List<FileSystemInfo>();
        try
        {
            files = dir.GetFileSystemInfos();
        }
        catch (UnauthorizedAccessException) { } //ignore
        catch (PathTooLongException)
        {
            MessageBox.Show("Path too long in directory: " + dir.FullName);
        }

        foreach (FileSystemInfo x in files)
        {
            DirectoryInfo dirInfo = x as DirectoryInfo;
            if (dirInfo != null)
            {
                foreach (FileInfo f in GetFilesRecursiveEnumerable(dirInfo))
                {
                    yield return f;
                }
            }
            else
            {
                FileInfo fInfo = x as FileInfo;
                if (fInfo != null) yield return fInfo;
            }
        }
    }

此方法会阻止 GUI。我想在后台线程中运行它(仅限单个),以便当FileSystemInfo 对象可用时,它会提供给调用者。

我已经能够让这个方法在后台工作程序中运行并返回FileSystemInfos 中的ICollection - 但这会返回整个列表,而我想在找到项目时生成它们。

编辑

似乎我可能需要重新评估我想要实现的目标(也许这需要回调而不是 IEnumerable)

基本上,我希望索引一个驱动器价值的文件,但我想在后台线程中运行它。这样,我可以逐个文件处理(可能是 Dir by Dir),如果需要,我可以在稍后阶段恢复该过程。如此有效,我希望调用者(GUI 线程)运行此方法,但在目录扫描期间收到通知,而不是在扫描完成时收到通知。比如,

//My original thoughts, but maybe need to tackle this a different way
public void ScanDrive()
{
  foreach(FileInfo f in GetFilesRecursiveEnumerable())
  {
     //index file
     //record the directory I am up to so I can resume later
     /Keeping my application responsive to perform other tasks
  }
}

【问题讨论】:

  • 事实上,这将运行得如此之快,以至于您必须放慢整个过程才能让用户看到更新。这是毫无意义。而且该模式与您的要求不匹配——您需要某种回调而不是收益。并且考虑到您必须将对回调的每个调用编组到 UI 线程上,您最终会在 UI 线程上运行此方法。免得惹恼自己。
  • 用户不会看到更新,而是我要索引文件。我不想将整个 Drive 的文件传递给调用者,而是希望以增量方式执行它们,这样我就有可能恢复驱动器的索引。
  • 您似乎遇到了不同的问题。不确定索引是什么意思,但您可能想要edit 并添加对您实际尝试完成的内容的描述。可能会得到一些更好的答案。
  • This blog post 有很多关于这个特定主题的信息,也许您可​​以根据自己的需要使用其中的一些信息。
  • 您需要在后台线程中运行调用它的代码,而不是单独运行此代码。

标签: c# backgroundworker yield


【解决方案1】:

yield 将控制权返回给 caller,直到 caller 再次调用枚举器,因此链中的下一个值(如果有)是 yielded 调用者

所以我想说,yield 本身不符合您的要求。为了实现您的想法,您可以使用简单的线程同步,其中调用者(主线程)在另一个线程上调用返回下一个文件信息的方法。

或者干脆使用一些“回调机制”:

简单示例:

void Main() {


   //start thread
    Thread t = new Thread(GetFilesRecursiveEnumerable)
    t.Start(..);
}


//called by thread as soon as FileInfo is ready
void FileReady(FileInfo fi) {
   ...
}
...

在方法内部:

public IEnumerable<FileInfo> GetFilesRecursiveEnumerable(DirectoryInfo dir)
{
    ...
    foreach (FileSystemInfo x in files)
    {
        DirectoryInfo dirInfo = x as DirectoryInfo;
        if (dirInfo != null)
        {
            foreach (FileInfo f in GetFilesRecursiveEnumerable(dirInfo))
            {
                FileReady(f);
            }
        }
        ...
    }
}

该代码自然无法用于生产,提供它只是为了让您对主题有所了解。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-07-12
    • 2012-10-09
    • 1970-01-01
    • 2017-01-23
    • 1970-01-01
    • 2021-01-22
    • 2019-08-18
    • 1970-01-01
    相关资源
    最近更新 更多