Подтвердить что ты не робот

Является ли это использование безопасности Parallel.ForEach() безопасным?

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

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. Есть ли что-то в этом роде?

Примечания:

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

Update:

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

4b9b3361

Ответ 1

Да; List<T> не является потокобезопасным, поэтому добавление к нему ad-hoc из произвольных потоков (возможно, в то же время) обречено. Вместо этого вы должны использовать потокобезопасный список или добавить блокировку вручную. Или, возможно, есть 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 для обновления другого индекса массива для каждого параллельного вызова.

Ответ 2

Есть ли что-то в этом роде неправильно?

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

Ответ 3

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

Элементы public static (Shared in Visual Basic) этого типа являются потокобезопасными. Любые члены экземпляра не гарантируют безопасность потоков.

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

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