【问题标题】:Howto program thread-based parallel list iteration?如何编写基于线程的并行列表迭代?
【发布时间】:2010-11-05 13:24:31
【问题描述】:

我需要一个例子,如何使用 ocaml-threads 编写并行迭代函数。我的第一个想法是有一个类似的功能:

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 ;;

那么并行迭代器可能看起来像这样(这里是基于进程的变体):

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 的 thread 模块来编码呢?

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

【问题讨论】:

  • 您已经知道,如果您将模块Thread 与 INRIA 的 OCaml 一起使用,您不会从并行性中获得任何加速?这是一个有趣的编程练习,只是不实用。
  • 是的,我知道一般垃圾收集器中缺少并行性。但我仍然希望有一个更好的 ocaml 实现。但是,如果我使用线程在硬盘上写东西,我已经看到线程在 linux 的 htop 中工作。您能否详细解释一下 ocaml 有哪些限制(或指出这些信息)?上面的问题是要了解Threads-module的正确用法。
  • 我把解释放在下面的答案中,给了自己更多的空间。

标签: multithreading iteration ocaml parallel-processing


【解决方案1】:

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

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

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

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

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

【讨论】:

    【解决方案2】:

    回答 cmets 提出的问题。答案并不完全适合评论本身。

    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.
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-07-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-02-20
      • 1970-01-01
      相关资源
      最近更新 更多