1

我正在编写一个网络链接检查程序,并遇到了我无法解释的 Interlocked 行为。首先,这是代码的精简版:

public class LinkCheckProcessor
{
    private long _remainingLinks;

    public event EventHandler<LinksCheckedEventArgs> AllLinksChecked;

    private void ProcessLinks(List<Link> links)
    {
        foreach (Link link in links)
        {
            ProcessLink(link);
        }
    }

    private void ProcessLink(Link link)
    {
        var linkChecker = new LinkChecker(link);
        linkChecker.LinkChecked += LinkChecked;
        Interlocked.Increment(ref _remainingLinks);
#if DEBUG
        System.Diagnostics.Debug.WriteLine(String.Format("LinkChecker: Checking link '{0}', remaining: {1}", link, Interlocked.Read(ref _remainingLinks)));
#endif
        linkChecker.Check();
    }

    void LinkChecked(object sender, LinkCheckedEventArgs e)
    {
        var linkChecker = (LinkChecker)sender;

        Interlocked.Decrement(ref _remainingLinks);
#if DEBUG
        System.Diagnostics.Debug.WriteLine(String.Format("LinkChecker: Checked link '{0}', remaining: {1}", linkChecker.Link, Interlocked.Read(ref _remainingLinks)));
#endif
        if (Interlocked.Read(ref _remainingLinks) == 0)
        {
            OnAllLinksChecked(new LinksCheckedEventArgs(this.BatchId, this.Web.Url));
        }
    }
}

我在调试输出中看到的是:

  • LinkChecker:检查链接“http://serverfault.com”,剩余:1
  • LinkChecker:已检查链接“http://superuser.com”,剩余:0
  • LinkChecker:检查链接“http://stackoverflow.com”,剩余:-1

我不明白为什么(在某些代码运行中)_remainingLinks变得消极。AllLinksChecked这也具有导致事件过早触发的副作用。(顺便说一句,上面的代码包含唯一_remainingLinks被触及的地方。)

我究竟做错了什么?

4

2 回答 2

3

您的AllLinksChecked逻辑肯定是错误的,因为计数器可能会变为0->1, 1->0, 0->1, 1->0, 0->1,1->0从而多次达到零。

但我不明白计数怎么会变成负数。这些是_remainingLinks您的代码中唯一出现的地方吗?


第一个问题可以通过删除增量代码ProcessLink并在开始循环之前ProcessLinks初始化计数来解决:links.Count

Interlocked.Exchange(ref _remainingLinks, links.Count)`

links参数不是在ProcessLinks运行时从其他线程写入的,是吗?

于 2011-05-22T16:36:36.930 回答
1

我打算冒昧地提出要LinkChecker触发多个事件来调用Check(). 没有这个,我看不出这个值怎么可能变成负数。

于 2011-05-22T16:55:45.050 回答