Обнаружение неожиданного блокированного поведения? - PullRequest
1 голос
/ 22 мая 2011

Я пишу программу для проверки веб-ссылок и сталкиваюсь с поведением с 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));
        }
    }
}

В выводе отладки я вижу такие вещи:

Я не понимаю, почему (в некоторых прогонах кода) _remainingLinks становится отрицательным. Это также имеет побочный эффект, вызвавший слишком ранний запуск события AllLinksChecked. (Кстати, приведенный выше код содержит только те места, к которым прикоснулся _remainingLinks.)

Что я делаю не так?

Ответы [ 2 ]

3 голосов
/ 22 мая 2011

Ваша логика 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, не так ли?

1 голос
/ 22 мая 2011

Я собираюсь выйти на конечность и предложить, чтобы LinkChecker запускал более одного события для вызова на Check(). Если не считать этого, я не вижу, как значение может стать отрицательным.

...