【问题标题】:Correct way of checking when ThreadPool threads are done?检查ThreadPool线程何时完成的正确方法?
【发布时间】:2012-07-07 02:44:35
【问题描述】:

我正在寻找一种方法来检查线程池中的所有线程何时完成其任务。目前我正在使用一个计数器,当线程完成它的工作以及counter == 0 我正在调用我的WorkComplete 方法时,它会递减。这似乎可行,但是当我到达最后的“工作”时,它似乎没有处理结果?或者至少用户界面没有得到它。这是我目前拥有的:

排队工作项 + 递增计数器

foreach (string s in URLs)
{
       ThreadPool.QueueUserWorkItem(new WaitCallback(DoWork), s);
       Interlocked.Increment(ref counter);
}

工作:

public void DoWork(object sender)
{      
    lock (_threadLock)
    {
        try
        {
            string url = (string)sender;
            result.URL = url;
            if (chkFb.Checked)
            {
                 result.Shares = grabber.GetFacebookShares(url);
            }
            if (chkTwitt.Checked)
            {
                 result.Tweets = grabber.GetTweetCount(url);
            }
            if (chkPlusOne.Checked)
            {
                 result.PlusOnes = grabber.GetPlusOnes(url);
            }
            Interlocked.Decrement(ref counter);
            this.Invoke(new ThreadDone(ReportProgress), result);
        }
        catch (Exception exc)
        {
            MessageBox.Show(string.Format("Errror: {0}", exc.Message);
        }
        finally
        {
            if (counter == 0)
            {
                this.Invoke(new ThreadDone(ReportProgress), result);
                this.Invoke(new Complete(WorkComplete));
            }
        }
    }
}

但是处理的 URL 数量总是比总数少 1,这几乎就像最后一个线程不是“报告回来”之类的。有人有什么想法吗?

谢谢

【问题讨论】:

  • 您是否在某处遇到异常?如果是这样,您的 Interlocked.Decrement 调用将不会针对该元素发生...

标签: c# multithreading threadpool interlocked


【解决方案1】:

上面的代码有几个问题:

  1. 您包含异常处理,但您对Interlocked.Decrement 的调用不在 finally 块中。这意味着异常将阻止jobCounter 正确减少。
  2. 您在您的方法中锁定了每个线程。这有效地使得这些 ThreadPool 线程中只有一个可以在任何时间点执行,因为它们都锁定在同一个变量 (_threadLock) 上。如果需要,没有理由使用多个线程池线程 - 只需让一个线程处理循环中的所有项目,因为这实际上就是您现在正在做的事情。
  3. 看起来(尽管代码不是 100% 清晰)您正在直接从线程池线程访问 UI 元素(即:chkTwitt.Checked)。这不可靠。
  4. 您将所有内容设置为单个result 变量,该变量在所有线程之间共享。在实际意义上,尚不清楚如何使用它。

鉴于您实际上只是在处理一组项目 (URL),您可能还需要考虑使用 Parallel.ForEach 甚至 PLINQ 来处理这些项目。

【讨论】:

  • 我可以改用lock(this) 吗?
  • @Duane 你为什么要使用锁定?
  • 我正在使用 result 在我的 ReportProgress 方法中更新 UI 线程上的网格视图,例如outputGrid.Rows.Add(result.URL, result.Shares, result.Tweets, result.PlusOnes); 每次调用 ReportProgress 时,我都会传入处理后的结果并在 gv 中添加一个新行
  • 我正在使用锁定来防止线程访问其他人正在使用的代码,没有它我会多次处理相同的 URL
  • @Duane 在这种情况下,绝对没有理由使用多个线程。您只是增加了没有任何好处的开销。只需使用一个线程池线程,然后循环运行 URL...
【解决方案2】:

好吧,我解决了我遇到的问题。

ScrapeResult result = new ScrapeResult();
string url = (string)sender;
result.URL = url;

if (chkFb.Checked)
{
    result.Shares = grabber.GetFacebookShares(url);
}
if (chkTwitt.Checked)
{
    result.Tweets = grabber.GetTweetCount(url);
}
if (chkPlusOne.Checked)
{
    result.PlusOnes = grabber.GetPlusOnes(url);
}

Interlocked.Decrement(ref counter);
this.Invoke(new ThreadDone(ReportProgress), result);

我没有在我的DoWork 方法中重用相同的变量result,而是为每个线程创建一个新变量,这样线程就不可能获取任何旧的/已处理的数据(因为我为每个线程创建了一个新实例)一)。这也解决了我在 UI 上多次显示结果时遇到的一些问题。

我还能够移除锁(一开始没有任何意义),并且通过这次经历了解了更多关于多线程的知识:)

【讨论】:

    猜你喜欢
    • 2011-03-11
    • 1970-01-01
    • 1970-01-01
    • 2011-12-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多