Параметры потока C# меняются во время выполнения потока - почему?

Итак, у меня есть метод, который получает словарь List, затем циклически перебирает ключи словаря и передает каждый список в отдельный поток.

Вот некоторый код / ​​псевдо-код:

public static void ProcessEntries() {

    Dictionary<string, List<myObj>> myDictionary = GetDictionary();

    foreach(string key in myDictionary.keys)
    {

        List<myObj> myList = myDictionary[key];

        Thread myThread = new System.Threading.Thread(new System.Threading.ThreadStart(delegate() {

            ProcessList(myList);

        }    
    }
}

public static void ProcessList(List<myObj> myList) {

    // Process entries
    // read-only operations on myList

}

Проблема в том, что во время выполнения ProcessList параметр myList просто изменяется.

Я перебрал список перед тем, как нажать на нить, а затем сразу внутри нити, и обнаружил, что результаты отличаются.

С тех пор я решил проблему (я думаю!), Сделав переменную Dictionary глобальной. Использование свойства [ThreadStatic] является следующим в списке возможных исправлений.

Что я действительно хочу знать, так это то, почему объект myList изменяется внутри ProcessList(), предположительно, когда объект myList переназначается в ProcessEntries()? Разве это не два разных списка? Если вся передача параметров по умолчанию выполняется по значению, почему функция ProcessList() не имеет локальной копии myList? (Является ли?)

Есть ли способ указать, что вы хотите передать параметр потоку, и чтобы он не изменялся родительским потоком или другими потоками во время выполнения? (Это будет похоже на атрибут [ThreadSafe] для глобальных переменных)

3 ответа

Я подозреваю, что ваш псевдокод на самом деле не является точным отражением вашего реального кода. Я подозреваю, что ваш реальный код выглядит так:

foreach(var pair in myDictionary)
{
    Thread myThread = new Thread(delegate() {
        ProcessList(pair.Value);
    });
    myThread.Start();
}

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

Чтобы исправить это, нужно сделать код более похожим на ваш псевдокод:

foreach(var pair in myDictionary)
{
    // You'll get a new list variable on each iteration
    var list = pair.Value;
    Thread myThread = new Thread(delegate() {
        ProcessList(list);
    });
    myThread.Start();
}

См . Блог Эрика Липперта об этом для получения дополнительной информации.

Если дело не в этом, приведите реальный пример, а не псевдокод. Короткий, но полный пример, демонстрирующий проблему, был бы идеальным.

Также убедитесь, что другие потоки не влияют на поток, который вы пытаетесь использовать. Обязательно используйте замки и мониторы... Были некоторые проблемы с этим всего несколько недель назад..

В этом случае вы передаете ссылку по значению, поэтому, если вы ее где-то измените, она будет отличаться везде.

Другие вопросы по тегам