遇到意外的联锁行为?

时间:2011-05-22 16:28:13

标签: c# .net multithreading interlocked

我正在编写一个Web链接检查器程序,并遇到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被触摸的地方。)

我做错了什么?

2 个答案:

答案 0 :(得分:3)

您的AllLinksChecked逻辑肯定是错误的,因为计数器可以转到0->11->00->11->00->1,{ {1}}因此多次达到零。

但我不知道伯爵怎么会变负面。这些是1->0出现在代码中的唯一地方吗?


第一个问题可以通过从_remainingLinks移除增量代码并让ProcessLink在开始循环之前将计数初始化为ProcessLinks来解决:

links.Count

Interlocked.Exchange(ref _remainingLinks, links.Count)` 运行时,links参数不是从其他线程写入的,是吗?

答案 1 :(得分:1)

我打算走出困境,并建议LinkCheckerCheck()号召唤多个事件。没有这个,我看不出这个价值可能会变成负数。