遇到意外的联锁行为?

发布于 2024-11-09 04:41:09 字数 1724 浏览 0 评论 0原文

我正在编写一个网络链接检查程序,并遇到了我无法解释的 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: Checked link 'http://serverfault.com', left: 1
  • LinkChecker: Checked link 'http://superuser .com',剩余:0
  • LinkChecker:检查链接“http://stackoverflow.com”,剩余:-1

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

我做错了什么?

I'm writing a web link checker program and encountering behaviour with Interlocked that I can't explain. First, here's an abridged version of the code:

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

What I'm seeing in the debug output are things like:

  • LinkChecker: Checked link 'http://serverfault.com', remaining: 1
  • LinkChecker: Checked link 'http://superuser.com', remaining: 0
  • LinkChecker: Checked link 'http://stackoverflow.com', remaining: -1

I don't understand why (in some code runs) _remainingLinks is becoming negative. This is also having the side effect of causing the AllLinksChecked event from being fired too early. (By the way, the code above contains the only places that _remainingLinks is touched.)

What am I doing wrong?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

一笔一画续写前缘 2024-11-16 04:41:09

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

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


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

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

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

Your AllLinksChecked logic is definitely wrong, since the counter could go 0->1, 1->0, 0->1, 1->0, 0->1, 1->0 and thus reach zero multiple times.

But I don't see how the count could ever go negative. Are these the only places that _remainingLinks appears in your code?


The first problem could be fixed just by removing the increment code from ProcessLink and having ProcessLinks initialize the count to links.Count before starting the loop:

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

The links argument isn't being written from other threads while ProcessLinks is running, is it?

夏九 2024-11-16 04:41:09

我将冒险提出,LinkChecker 会为调用 Check() 触发多个事件。除此之外,我看不出该值怎么可能变成负值。

I'm going to go out on a limb and propose that LinkChecker is firing more than one event for a call to Check(). Short of this, I can't see how the value could possibly go negative.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文