0

我正在寻找一种检查线程池中的所有线程何时完成任务的方法。目前我正在使用一个计数器,当线程完成它的工作以及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 的数量总是比总数少一个,这几乎就像最后一个线程不是“报告回来”之类的。有没有人有任何想法?

谢谢

4

2 回答 2

3

上面的代码有几个问题:

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

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

于 2012-07-06T16:00:46.470 回答
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 上多次显示结果时遇到的一些问题。

我还能够移除锁(这首先是没有意义的)并且通过这次经历学到了更多关于多线程的知识:)

于 2012-07-10T08:06:45.193 回答