Это не безопасно.У вас есть условие состязания, код работает из-за вашего конкретного времени и потому, что функция add
зависит от того, как реализован планировщик (который может быть изменен).
Вам нужно помнить, чтоконцептуально ваша нить может быть приостановлена в любое время.Предположим, что поток приостановлен после доступа к !x
или после вычисления (!x+1)
или между присваиванием и printf
.Если другой поток включится и выполнит всю функцию до того, как приостановленной будет разрешено продолжить, результат будет неправильным.
Один из способов показать проблему - заменить функцию добавления на:
let add i x =
x := !x + i;
for i = 1 to Random.int 1000 do ignore (Unix.getaddrinfo "localhost" "" []) done;
Printf.printf "add %d: x is now %d\n" i !x;
flush stdout
(более простой способ - вставить случайное значение Thread.delay
между присваиванием и printf, но с помощью приведенного выше вы можете видеть, что «обычные» вычисления могут привести к проблеме)
Вотрезультат на моей машине:
> ./test.native
add 10: x is now 11
add 1: x is now 11
add 10: x is now 21
add 1: x is now 32
add 10: x is now 32
add 1: x is now 43
Вы должны убедиться, что сложение, присвоение и printf
выполняются «атомно» потоком («атомарно» в том смысле, что если поток вприостановлено в этом теле кода, никто не должен иметь права вводить его).Один из способов сделать это - использовать мьютекс:
let add =
let m = Mutex.create () in
fun i x ->
Mutex.lock m;
try
x := !x + i;
for i = 1 to Random.int 1000 do ignore (Unix.getaddrinfo "localhost" "" []) done;
Printf.printf "add %d: x is now %d\n" i !x;
flush stdout;
Mutex.unlock m;
with e -> Mutex.unlock m; raise e
Обратите внимание, что это работает для приведенного вами примера, но предполагает, что только add
изменяет x
.Для более подробной информации по теме я рекомендую вам прочитать:
http://ocamlunix.forge.ocamlcore.org/threads.html
и, в частности:
http://ocamlunix.forge.ocamlcore.org/threads.html#htoc64