Небезопасная работа со словарем - PullRequest
3 голосов
/ 23 декабря 2011

Итак, в нижней части http://msdn.microsoft.com/en-us/library/xfhwa508.aspx мы видим

Словарь (Of TKey, TValue) может поддерживать несколько читателей одновременно, если коллекция не изменена. Тем не менее, перечисление в коллекции по сути не является потокобезопасной процедурой. В редком случае, когда перечисление конкурирует с доступом для записи, коллекция должна быть заблокирована в течение всего перечисления. Чтобы обеспечить доступ к коллекции из нескольких потоков для чтения и записи, необходимо реализовать собственную синхронизацию.

В моем приложении asp.net я иногда вижу исключения NullReferenceException, сгенерированные изнутри System.Collections.Generic.Dictionary`2.Insert.

Как я мог воссоздать это в песочнице?

Следующий код не вызывает исключение, несмотря на то, что локальный словарь доступен отдельным потокам.

class Program {
    static void Main(string[] args) {
        int maxThreads = 20;
        Dictionary<Guid, Guid> dict = new Dictionary<Guid, Guid>();
        var tws = new threadWithState();
        while (true) {
            for (int i = 0; i < maxThreads * 2; i++) {
                var thread = new Thread(tws.Work);
                thread.IsBackground = true;
                thread.Start(dict);
            }
            dict[Guid.NewGuid()] = Guid.NewGuid();
        }
    }
}

class threadWithState {
    public void Work(object dict) {
        Console.WriteLine((dict as Dictionary<Guid, Guid>).Count);
        for (int i = 0; i < 1000; i++) {
            (dict as Dictionary<Guid, Guid>)[Guid.NewGuid()] = Guid.NewGuid();
        }
    }
}

Ответы [ 2 ]

8 голосов
/ 23 декабря 2011

Это часть проблемы с многопоточностью - ошибки не обязательно воспроизводимы надежно.Проблема в том, что вставка должна попасть в определенное состояние гонки, чего явно не происходит в вашем тесте.Конечно, достаточное количество прогонов может привести к тому, что это сработает, но это будет сложно проверить достоверно.

При этом, очевидно, это проблема.Вместо того, чтобы сосредоточиться на воспроизведении, вы должны подумать, как это исправить.Очевидный ответ здесь, конечно, состоит в том, чтобы переключиться на другую технику, например, вместо этого использовать ConcurrentDictionary<T,U>, который является поточно-ориентированным.

3 голосов
/ 23 декабря 2011

Этот код страдает от случайной синхронизации из-за блокировки кучи, собираемой мусором. Вы получите гораздо более быстрый сбой, введя случайные задержки, очень похожие на то, что происходит на реальном сервере. Этот код вылетел на моей машине за доли секунды, ymmv:

        public void Work(object dict) {
            var rnd = new Random();
            Console.WriteLine((dict as Dictionary<Guid, Guid>).Count);
            for (int i = 0; i < 1000000; i++) {
                (dict as Dictionary<Guid, Guid>)[Guid.NewGuid()] = Guid.NewGuid();
                for (int ix = 0; ix < rnd.Next(1000); ++ix) ;   // NOTE: random delay
            }
        }
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...