簡體   English   中英

等待所有線程完成

[英]Wait for all threads to finish

我想為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#的新手,也不是並行編程專家,因此可能會有一些重量級的概念錯誤)

您固有的通用方法固有地使這個問題很難解決。 相反,您可以簡單地使用文件系統方法為您遍歷層次結構,然后使用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
});

您將要研究Task.WaitAll和Task.WaitAny方法。 這里有示例代碼: msdn.microsoft.com

快速答案:

Task.WaitAll(subTasks);

應該為您工作。

在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:還有很多提升性能的空間

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM