Это использование потока Parallel.ForEach () безопасно? - PullRequest
21 голосов
/ 09 апреля 2011

По сути, я работаю с этим:

var data = input.AsParallel();
List<String> output = new List<String>();

Parallel.ForEach<String>(data, line => {
    String outputLine = ""; 
    // ** Do something with "line" and store result in "outputLine" **

    // Additionally, there are some this.Invoke statements for updating UI

    output.Add(outputLine);
});

Вводом является List<String> объект.Оператор ForEach() выполняет некоторую обработку каждого значения, обновляет пользовательский интерфейс и добавляет результат в output List.Есть ли что-то не так с этим?

Примечания:

  • Порядок вывода неважно

Обновление:

На основании полученных отзывов я добавил руководство lock в оператор output.Add, а также в код обновления пользовательского интерфейса.

Ответы [ 3 ]

29 голосов
/ 09 апреля 2011

Да;List<T> не является потокобезопасным, поэтому добавление к нему произвольных потоков из произвольных потоков (вполне возможно, в то же время) обречено.Вместо этого вы должны использовать потокобезопасный список или добавить блокировку вручную.Или, может быть, есть Parallel.ToList.

Также, если это имеет значение: порядок вставки не гарантируется.

Эта версия безопасна , хотя:

var output = new string[data.Count];

Parallel.ForEach<String>(data, (line,state,index) =>
{
    String outputLine = index.ToString();
    // ** Do something with "line" and store result in "outputLine" **

    // Additionally, there are some this.Invoke statements for updating UI
    output[index] = outputLine;
});

здесь мы используем index для обновления другого индекса массива для параллельного вызова.

10 голосов
/ 09 апреля 2011

Что-то в этом не так?

Да, все. Ничто из этого не безопасно. Списки не безопасны для одновременного обновления в нескольких потоках, и вы не можете обновить пользовательский интерфейс из любого потока, кроме потока пользовательского интерфейса.

3 голосов
/ 09 апреля 2011

В документации говорится следующее о безопасности потоков List<T>:

Открытые статические (Shared в Visual Basic) члены этого типа являются поточно-ориентированными.Ни один из членов экземпляра не гарантированно является потокобезопасным.

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

Таким образом, output.Add(outputLine) является не поточно-ориентированным, и вам необходимочтобы обеспечить безопасность потоков самостоятельно, например, добавив операцию add в оператор lock.

...