Если вы собираетесь попробовать симуляцию, вам понадобится намного больше контроля над вашими узлами, чем простое использование потоков позволит & mdash; или, по крайней мере, без серьезных болей.
Мой субъективный подход к этой теме - создать простую однопоточную виртуальную машину, чтобы сохранить полный контроль над симуляцией. Самый простой способ сделать это в OCaml - использовать монадоподобную структуру (как, например, в Lwt):
(* A thread is a piece of code that can be executed to perform some
side-effects and fork zero, one or more threads before returning.
Some threads may block when waiting for an event to happen. *)
type thread = < run : thread list ; block : bool >
(* References can be used as communication channels out-of-the box (simply
read and write values ot them). To implement a blocking communication
pattern, use these two primitives: *)
let write r x next = object (self)
method block = !r <> None
method run = if self # block then [self]
else r := Some x ; [next ()]
end
let read r next = object (self)
method block = !r = None
method run = match r with
| None -> [self]
| Some x -> r := None ; [next x]
end
Вы можете создавать лучшие примитивы, которые соответствуют вашим потребностям, такие как добавление свойства "время, необходимое для передачи" в ваших каналах.
Следующим шагом является определение механизма моделирования.
(* The simulation engine can be implemented as a simple queue. It starts
with a pre-defined set of threads and returns when no threads are left,
or when all threads are blocking. *)
let simulate threads =
let q = Queue.create () in
let () = List.iter (fun t -> Queue.push t q) threads in
let rec loop blocking =
if Queue.is_empty q then `AllThreadsTerminated else
if Queue.length q = blocking then `AllThreadsBlocked else
let thread = Queue.pop q in
if thread # block then (
Queue.push thread q ;
loop (blocking + 1)
) else (
List.iter (fun t -> Queue.push t q) (thread # run) ;
loop 0
)
in
loop 0
Опять же, вы можете настроить движок так, чтобы он отслеживал, какой узел выполняет какой поток, чтобы сохранять приоритеты для каждого узла, чтобы имитировать, что один узел значительно медленнее или быстрее других, или случайным образом выбирать поток для выполнения на каждом шаг и так далее.
Последний шаг - выполнение симуляции. Здесь у меня будет два потока, посылающих случайные числа взад и вперед.
let rec thread name input output =
write output (Random.int 1024) (fun () ->
read input (fun value ->
Printf.printf "%s : %d" name value ;
print_newline () ;
thread name input output
))
let a = ref None and b = ref None
let _ = simulate [ thread "A -> B" a b ; thread "B -> A" b a ]