当前位置: 首页 > 知识库问答 >
问题:

等待所有线程完成

程祯
2023-03-14

我想在C#中处理子目录和文件的文件系统/文件夹。我正在使用TPL库中的任务。这个想法是递归地执行它并为每个文件夹创建一个任务。主线程应该等待子线程完成,然后打印一些信息。事实上我只是想知道扫描何时完成。我已经开始使用线程池,然后切换到TLP。做了一些简单的例子。经过一些尝试从简单的代码到越来越臃肿的代码我被困在这里:

private Logger log = LogManager.GetCurrentClassLogger();

public MediaObjectFolder MediaObjectFolder { get; set; }
private Queue<MediaObjectFolder> Queue { get; set; }

private object quelock, tasklock;
private List<Task> scanTasks;

public IsoTagger()
{
    quelock = new object();
    tasklock = new object();
    scanTasks = new List<Task>();

    MediaObjectFolder = new MediaObjectFolder(@"D:\Users\Roman\Music\Rock\temp");
    Queue = new Queue<MediaObjectFolder>();
}

public MediaObject RescanFile(string fullpath, string filename)
{
    return new MediaObject(fullpath);
}

public void Rescan()
{
    Queue.Clear();

    lock (tasklock)
    {
        Task scanFolderTask = Task.Factory.StartNew(ScanFolder, MediaObjectFolder);
        scanTasks.Add(scanFolderTask);
    }

    Task.Factory.ContinueWhenAll(scanTasks.ToArray(), (ant) =>
        {
            if (log != null)
            {
                log.Debug("scan finished");
                log.Debug("number of folders: {0}", Queue.Count);
            }

        });
}

private void ScanFolder(object o)
{
    List<Task> subTasks = new List<Task>();

    MediaObjectFolder mof = o as MediaObjectFolder;
    log.Debug("thread - " + mof.Folder);

    string[] subdirs = Directory.GetDirectories(mof.Folder);
    string[] files = Directory.GetFiles(mof.Folder, "*.mp3");


    foreach(string dir in subdirs)
    {
        log.Debug(dir);

        MediaObjectFolder tmp = new MediaObjectFolder(dir);
        lock (tasklock)
        {
            Task tmpTask = new Task(ScanFolder, tmp);
            subTasks.Add(tmpTask);
        }
    }

    foreach (Task tsk in subTasks)
    {
        tsk.Start();
    }

    foreach (string file in files)
    {
        log.Debug(file);

        MediaObject tmp = new MediaObject(file);
        MediaObjectFolder.MediaObjects.Add(tmp);
    }

    lock (quelock)
    {
        Queue.Enqueue(mof);
    }

    if (subTasks != null)
        Task.Factory.ContinueWhenAll(subTasks.ToArray(), logTask => log.Debug("thread release - " + mof.Folder));
}

主线程有时仍然过早地继续,而不是在完成所有其他线程之后继续。(我对C#比较陌生,也不是并行编程方面的专家,所以可能会有一些严重的概念错误)

共有3个答案

洪哲彦
2023-03-14

经过Servy的好建议和对C#中并行性的进一步研究,我想出了我问题的答案。因为我真的不需要LINQ来完成这个简单的任务,我只想枚举我的文件系统并并行处理文件夹。

public void Scan()
{
    // ...
    // enumerate all directories under one root folder (mof.Folder)
    var directories = Directory.EnumerateDirectories(mof.Folder, "*", SearchOption.AllDirectories);
    // use parallel foreach from TPL to process folders
    Parallel.ForEach(directories, ProcessFolder);
    // ...
}

private void ProcessFolder(string folder)
{
    if (!Directory.Exists(folder))
    {
        throw new ArgumentException("root folder does not exist!");
    }
    MediaObjectFolder mof = new MediaObjectFolder(folder);
    IEnumerable<string> files = Directory.EnumerateFiles(folder, "*.mp3", SearchOption.TopDirectoryOnly);
    foreach (string file in files)
    {
        MediaObject mo = new MediaObject(file);
        mof.MediaObjects.Add(mo);
    }
    lock (quelock)
    {
         // add object to global queue
         Enqueue(mof);
    }
}

经过相当深入的研究,我发现这是最简单的解决方案。请注意:如果这种方法更快,我还没有做任何测试,因为我在一个不是很大的临时文件库上工作。这也是MSDN库中描述的并行处理文件系统的方式。

PS:性能也有很大的提升空间

郎慎之
2023-03-14

你会想研究这个任务。WaitAll和Task。WaitAny方法。这里有一个示例代码:msdn。微软通用域名格式

快速回答:

Task.WaitAll(subTasks);

应该对你有用。

钱锐
2023-03-14

您固有的一般方法使这成为一个相当难解决的问题。相反,您可以简单地使用文件系统方法为您遍历层次结构,然后使用PLINQ有效地并行处理这些文件:

var directories = Directory.EnumerateDirectories(path, "*"
    , SearchOption.AllDirectories);

var query = directories.AsParallel().Select(dir =>
{
    var files = Directory.EnumerateFiles(dir, "*.mp3"
        , SearchOption.TopDirectoryOnly);
    //TODO create custom object and add files
});
 类似资料:
  • 这可能是在类似的背景下问的,但我在搜索了大约20分钟后找不到答案,所以我会问。 我已经编写了一个Python脚本(比如说:scriptA.py)和一个脚本(比如说scriptB.py) 在scriptB中,我想用不同的参数多次调用scriptA,每次运行大约需要一个小时,(这是一个巨大的脚本,做了很多事情……不用担心),我希望能够同时使用所有不同的参数运行scriptA,但我需要等到所有参数都完成

  • 我打算在主线程中启动2个线程,主线程应该等到所有2个子线程完成,我就是这样做的。 在上面的代码中,确实让主线程等待子线程,但问题是,在第一个线程完成之前不会创建第二个线程。这不是我想要的。 我想要的是,这两个线程立即在主线程中创建,然后主线程等待它们完成。似乎做不到,是吗? 我想,也许我可以通过一个信号灯来完成这项工作,但还有别的方法吗?

  • 问题内容: 我需要一次执行一些任务4,如下所示: 全部完成后如何获得通知?现在,我想不出什么比设置一些全局任务计数器并在每个任务结束时减少它,然后无限循环监视此计数器为0更好的了。或获取期货的列表,并在无限循环中对所有期货进行isDone监视。什么是不涉及无限循环的更好的解决方案? 问题答案: 基本上在你致电之后:

  • 问题内容: 我有一个代码,其中4个线程同时运行。我想等到所有这4个线程都结束。并且只有在那之后才能继续应用程序流程。 我尝试了两种方法: ,这种方法可以正常工作。之后的代码仅在所有线程完成后才执行。 ,此技术允许执行代码,即使并非所有线程都已完成,也可以执行代码。 代码示例: 我的问题: 为什么并且不要等到所有线程都完成并打印出来 _«我们完成了! 所有线程都完成了!» _刚打完电话? 问题答案:

  • 我有一个生产者-消费者模式的多线程任务。可能有许多生产者和一个消费者。我使用ArrayBlockingQueue作为共享资源。 Producer类中的run()方法: Consumer类中的run()方法: main()方法: 现在,当队列为空时,我有消费者结束条件。但是可能会有一段时间队列变成空的,但是一些生产者线程仍然在工作。所以我只需要在完成所有生产者线程之后才完成消费者线程(但它们的数量事

  • 有时我看到一些线程还没有完成他们的工作,服务杀死那个线程,我怎么能强迫服务等待,直到线程完成他们的工作? 这是我的代码: 我看到了一些例外。future.is完成())块。我怎么能确保每一个未来是当执行者服务关闭?