Прошу простить мое слегка юмористическое звание. Я использую в нем два разных определения слова «безопасный» (очевидно).Безопасно ли использовать функции «Небезопасные» функции резьбы?
Я довольно новичок в резьбонарезке (ну, я использовал резьбу много лет, но только очень простые формы). Теперь я столкнулся с проблемой написания параллальных реализаций некоторых алгоритмов, а потоки должны работать с одними и теми же данными. Рассмотрим следующую ошибку новичка:
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
является довольно низкой (во многих случаях, но не все!), И поэтому он (возможно) глупо не играть безопасно. Но я также чувствую, что было бы хорошо знать, как это действительно работает на теоретическом уровне, поэтому я все еще думаю, что этот вопрос очень интересный.
Я предполагаю, что нижняя часть была вызвана юмористическим титулом. –