检查ThreadPool线程何时完成的正确方法?

时间:2012-07-06 15:52:50

标签: c# multithreading threadpool interlocked

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

排队工作项目+递增计数器

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总量总是比总数少一个,这几乎就像最后一个帖子不是“报告回来”或其他东西。有没有人有任何想法?

三江源

2 个答案:

答案 0 :(得分:3)

上述代码中存在一些问题:

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

答案 1 :(得分:0)

好吧,我解决了这个问题。

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);

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

我还能够移除锁定(首先没有任何意义),并通过以下经验了解了多线程:)