Skip to main content
 首页 » 编程设计

c#之遇到意外的联锁行为

2024年02月23日30小虾米

我正在编写一个 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 被触及的地方。)

我做错了什么?

请您参考如下方法:

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

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


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

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

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