如何编写基于线程的并行列表迭代?

时间:2010-11-05 13:24:31

标签: multithreading iteration ocaml parallel-processing

我需要以如何使用ocaml-threads编程并行iter函数为例。我的第一个想法是拥有一个与此类似的功能:

let procs = 4 ;;

let rec _part part i lst =   match lst with
      [] -> ()
    | hd::tl -> 
        let idx = i mod procs in
        (* Printf.printf "part idx=%i\n" idx; *)
        let accu = part.(idx) in
          part.(idx) <- (hd::accu);
          _part part (i+1) tl ;;

然后,并行iter可能看起来像这样(这里是基于流程的变体):

let iter f lst =   let part = Array.create procs [] in
    _part part 0 lst;
    let rec _do i =
      (* Printf.printf "do idx=%i\n" i; *)
      match Unix.fork () with
          0 -> (* Code of child *)
            if i < procs then 
              begin
                (* Printf.printf "child %i\n" i; *)
                List.iter f part.(i) 
              end
        | pid -> (* Code of father *)
            (* Printf.printf "father %i\n" i; *)
            if i >= procs then ignore (Unix.waitpid [] pid)
            else _do (i+1)
    in
      _do 0 ;;

因为Thread-module的使用有点不同,我将如何使用ocaml的线程模块对其进行编码?

还有另外一个问题,_part()函数必须扫描整个列表以将它们分成n个部分,然后每个部分将通过每个自己的进程(这里)进行管道传输。还是存在一个没有先拆分列表的解决方案吗?

2 个答案:

答案 0 :(得分:3)

如果您有一个处理列表的函数,并且您希望独立地在多个列表上运行它,则可以使用该函数和每个列表调用Thread.create。如果您将列表存储在数组part中,则:

let threads = Array.map (Thread.create (List.iter f)) part in
Array.iter Thread.join threads

INRIA OCaml线程不是实际线程:在任何给定时间只执行一个线程,这意味着如果您有四个处理器和四个线程,则所有四个线程将使用相同的处理器,而其他三个线程将保持未使用状态。

线程有用的地方在于它们仍然允许异步编程:一些Thread模块原语可以等待外部资源变得可用。这可以减少您的软件花费在不可用资源上的时间,因为您可以让另一个线程同时执行其他操作。您还可以使用它同时启动多个外部异步进程(例如通过HTTP查询多个Web服务器)。如果您没有很多与资源相关的阻止,这对您没有帮助。

关于列表拆分问题:要访问列表元素,必须遍历所有先前的元素。虽然这种遍历在理论上可以分散在多个线程或进程中,但是通信开销可能会比在一个进程中提前分割事物慢得多。或使用数组。

答案 1 :(得分:2)

回答评论中的问题。答案本身并不适合评论。

OCaml运行时存在锁定。当OCaml线程即将进入

的C函数时,将释放锁定
  • 可能阻止;
  • 可能需要很长时间。

所以你只能使用一个OCaml线程,但是你有时可以让非堆使用的C函数与它并行工作。

例如,请参阅文件ocaml-3.12.0/otherlibs/unix/write.c

memmove (iobuf, &Byte(buf, ofs), numbytes); // if we kept the data in the heap
                                            // the GC might move it from
                                            // under our feet.
enter_blocking_section();                   // release lock.
                                            // Another OCaml thread may
                                            // start in parallel of this one now.
ret = write(Int_val(fd), iobuf, numbytes);
leave_blocking_section();                   // take lock again to continue
                                            // with Ocaml code.