Безопасность потоков при совместном использовании ссылки между потоками POSIX в OCaml - PullRequest
2 голосов
/ 14 февраля 2011

Примитивный способ, позволяющий двум потокам совместно использовать состояние с типом int ref в OCaml:

let add i x =
    x := !x + i;
    Printf.printf "add %d: x is now %d\n" i !x;
    flush stdout

let rec run f x d =
    f x;
    Thread.delay d;
    run f x d

let _ =
    let x = ref 0 in
    ignore(Thread.create (run (add 1) x) 0.2);
    run (add 10) x 0.1

Функция add просто добавляет i к целому числу в ссылке x. Функция run просто продолжает применять f к одному и тому же аргументу x с задержками d между каждым приложением. Основная функция начинается с 0 как целое число в ссылке и вызывает функцию запуска параллельно с различными аргументами: (1) добавление 1 и задержка 0,2 с между вызовами; (2) добавление 10 и задержка 0,1 с между вызовами.

Результат запуска этой программы:

add 10: x is now 10
add 1: x is now 11
add 10: x is now 21
add 10: x is now 31
add 1: x is now 32
add 10: x is now 42
add 10: x is now 52
add 1: x is now 53
add 10: x is now 63
add 10: x is now 73
add 1: x is now 74
add 10: x is now 84
add 10: x is now 94
add 1: x is now 95
[...]

Легко видеть, что содержимое ссылки распределяется между потоками.

Я использовал эту конструкцию в случае, когда первая функция run выполняла численные вычисления, а другая функция run выводила числа, рассчитанные в первом потоке. Это сработало хорошо. В этом случае один из потоков записывал в состояние, а другой только читал. В приведенном выше примере оба потока пишут в состояние, которое, на мой взгляд, может быть проблематичным, но я точно не знаю, почему.

Мой вопрос

Мои вопросы по этому поводу:

(1) Эта конструкция небезопасна? Если так, у вас есть пример, где это может пойти не так? (2) Знаете ли вы о лучшей конструкции для достижения той же цели? Стоит ли использовать какие-то замки или сигналы?

Заранее спасибо за любую помощь!

Всего наилучшего, Surikator.

1 Ответ

3 голосов
/ 14 февраля 2011

Это не безопасно.У вас есть условие состязания, код работает из-за вашего конкретного времени и потому, что функция 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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...