8

Прошу простить мое слегка юмористическое звание. Я использую в нем два разных определения слова «безопасный» (очевидно).Безопасно ли использовать функции «Небезопасные» функции резьбы?

Я довольно новичок в резьбонарезке (ну, я использовал резьбу много лет, но только очень простые формы). Теперь я столкнулся с проблемой написания параллальных реализаций некоторых алгоритмов, а потоки должны работать с одними и теми же данными. Рассмотрим следующую ошибку новичка:

const 
    N = 2; 

var 
    value: integer = 0;  

function ThreadFunc(Parameter: Pointer): integer; 
var 
    i: Integer; 
begin 
    for i := 1 to 10000000 do 
    inc(value); 
    result := 0; 
end; 

procedure TForm1.FormCreate(Sender: TObject); 
var 
    threads: array[0..N - 1] of THandle; 
    i: Integer; 
    dummy: cardinal; 
begin 

    for i := 0 to N - 1 do 
    threads[i] := BeginThread(nil, 0, @ThreadFunc, nil, 0, dummy); 

    if WaitForMultipleObjects(N, @threads[0], true, INFINITE) = WAIT_FAILED then 
    RaiseLastOSError; 

    ShowMessage(IntToStr(value)); 

end; 

Новичок может ожидать код, чтобы отобразить сообщение 20000000. Действительно, первый value равен 0, а затем мы inc это 20000000 раз. Однако, поскольку процедура inc не является «атомарной», эти два потока конфликтуют (я думаю, что inc выполняет три вещи: он читает, он увеличивает и сохраняет), и поэтому многие из inc s будут эффективно «потеряны» ». Типичным значением, которое я получаю из приведенного выше кода, является 10030423.

Простейшим обходным путем является использование InterlockedIncrement вместо Inc (что будет намного медленнее в этом глупом примере, но это не вопрос). Другим обходным решением является размещение inc внутри критического раздела (да, это также будет очень медленным в этом глупом примере).

Теперь, в большинстве реальных алгоритмов, конфликты не являются общими. На самом деле, они могут быть очень необычными. Один из моих алгоритмов создает DLA fractals, и одна из переменных, которые I inc время от времени является числом адсорбированных частиц. Конфликты здесь очень редки, и, что более важно, мне действительно все равно, если переменная суммирует до 20000000, 20000008, 20000319 или 19999496. Таким образом, заманчиво не использовать InterlockedIncrement или критические разделы, так как они просто раздувает код и делает его (незначительно) медленнее, чем нет (насколько я вижу).

Однако, мой вопрос: могут ли быть более серьезные последствия конфликтов, чем слегка «неправильное» значение увеличивающейся переменной? Может ли программа сбой, например?

Правда, этот вопрос может показаться глупым, потому что, в конце концов, стоимость использования InterlockedIncrement вместо inc является довольно низкой (во многих случаях, но не все!), И поэтому он (возможно) глупо не играть безопасно. Но я также чувствую, что было бы хорошо знать, как это действительно работает на теоретическом уровне, поэтому я все еще думаю, что этот вопрос очень интересный.

+0

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

ответ

10

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

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

Более эффективный подход заключается в том, чтобы каждый поток поддерживал свой собственный счет. Затем суммируйте все отдельные подсчеты потоков, когда вы присоединяетесь к потокам в конце вычисления. Таким образом, вы получаете лучшее из обоих миров. Нет возражений против приращения и правильного ответа. Конечно, вам необходимо принять меры для того, чтобы вас не уловили false sharing.

+0

Благодарим вас за ответ. Что касается последней части, я знаю, что это глупый пример, но я просто хотел получить подтверждение моей гипотезы о том, что худшее, что может произойти в таком случае, является очевидным неправильным (или «слегка-выключенным») результатом , –

+0

Риск, конечно, также основан на том, что вы делаете с этим счетом. Если это только для отображения, это не проблема, но это может быть проблемой, если вы используете его для итерации или другого материала, где неправильный счет может означать утечку памяти или доступ к памяти, которая была освобождена –

+0

@Marco: Конечно. Я просто боялся, что «конфликт» может быть потенциально опасным сам по себе. –

 Смежные вопросы

  • Нет связанных вопросов^_^