【问题标题】:How to know if all the Thread Pool's thread are already done with its tasks?如何知道线程池的所有线程是否已经完成了它的任务?
【发布时间】:2011-01-30 05:56:28
【问题描述】:

我有这个应用程序,它将递归给定目录中的所有文件夹并查找 PDF。如果找到 PDF 文件,应用程序将使用 ITextSharp 计算其页数。我通过使用一个线程递归地扫描所有文件夹中的 pdf 来做到这一点,然后如果找到 PDF,它将被排队到线程池中。代码如下所示:

//spawn a thread to handle the processing of pdf on each folder.
                var th = new Thread(() =>
                {
                    pdfDirectories = Directory.GetDirectories(pdfPath);
                    processDir(pdfDirectories);
                });
                th.Start();



 private void processDir(string[] dirs)
        {
            foreach (var dir in dirs)
            {
                pdfFiles = Directory.GetFiles(dir, "*.pdf");
                processFiles(pdfFiles);

                string[] newdir = Directory.GetDirectories(dir);
                processDir(newdir);
            }
        }



private void processFiles(string[] files)
        {
            foreach (var pdf in files)
            {
                ThreadPoolHelper.QueueUserWorkItem(
                    new { path = pdf },
                    (data) => { processPDF(data.path); }
                    );
            }
        }

我的问题是,我怎么知道线程池的线程已经完成了所有排队项的处理,所以我可以告诉用户应用程序已经完成了它的预期任务?

【问题讨论】:

    标签: c# .net multithreading threadpool


    【解决方案1】:

    通常我会通过一个计数器变量来做这样的事情。 对于您在ThreadPool 中排队的每个工作项,将一个添加到计数器变量。 然后当它被处理时,你会减少计数器变量。

    确保您通过Interlocked 类上的方法进行递增和递减,因为这将确保以线程安全的方式完成。

    一旦计数器达到零,您可以使用 ManualResetEvent 标记任务已完成

    如果您可以访问 .NET 4,那么您可以使用新的 CountdownEvent 类来做类似的事情。

    【讨论】:

    • 我做了类似的逻辑。只有我使用了 ThreadPool.GetAvailableThreads。我将池应该使用的 MaxThreads 设置为 20。所以在任务的最后,我检查了可用线程是否等于我设置的最大值。不幸的是,这失败了。我会研究那个手动重置事件。
    • @mcxiand:您不能使用 ThreadPool 计数器,因为其他代码 (lib) 可以创建线程并且 TP 正在回收/删除它们。
    • @Henk Holterman:是这样吗?据我了解,ThreadPool 将通过 QueueUserWorkItem 方法将任务排队。你能提供更多关于你的评论的细节吗?将不胜感激。
    • @mcxiand:你无法保证线程池中的所有线程在你处理完你的项目后都是空闲的。很可能在处理完您的项目后所有线程都可用,但是每个进程有一个 ThreadPool,因此加载的任何其他代码很可能同时使用 ThreadPool 执行操作,您的代码不应该永远依赖于关于线程数的假设。
    • 非常感谢您的解释。我已经明白了。我还按照 JonC 上面所说的那样更改了我的代码。
    【解决方案2】:

    1) 如何知道是否所有线程都结束了?

    您必须让线程自己签入/签出,方法是将您的代码括起来:

    Interlocked.Increment(ref jobCounter);
    // your code
    Interlocked.Decrement(ref jobCounter);
    

    如果这让您的匿名委托过于混乱,那么只需使用包装方法。您可能还必须添加异常处理。

    联锁方法仍然避免了等待它变为 0 的问题,使用 Sleep() 的循环是一个弱但在这种情况下可行的解决方案。

    2) 您正在递归 Tree walker 中启动线程。请注意,您可能创建了太多它们,这会影响性能。

    【讨论】:

    • 我只有一个线程来执行递归并将线程在每个文件夹中找到的每个pdf文件排队
    • 好的,它更线性。从 cmets 中,您可以限制 MaxThreads。仍然需要调整和更改 MaxThreads 是一个大锤。
    • 我控制了 MaxThreads,因为在这个线程上,我正在使用 BeginInvoke 更新 UI 控件。如果有很多线程正在运行,UI 将冻结。有什么建议吗?
    猜你喜欢
    • 2010-10-16
    • 2022-01-19
    • 2012-03-06
    • 1970-01-01
    • 2019-10-24
    • 2020-03-27
    • 1970-01-01
    • 2013-08-14
    • 1970-01-01
    相关资源
    最近更新 更多