我正在从网络上读取数据,而且每当我拿到它时,我都想把它写到文件中。写入是并发和非顺序的(想想P2P文件共享)。在C中,我将获得文件的文件描述符(在程序的持续时间内)然后使用this.componentRootDOM
,然后使用lseek
并最终关闭write
。这些操作可以在多线程设置中由互斥锁保护(尤其是lseek和write应该是原子的)。
我真的不知道如何在Async中获得这种行为。我最初的想法就是拥有这样的东西。
fd
然后,在接收数据时异步调度写入。
我的解决方案并不正确。首先,这个 let write fd s pos =
let posl = Int64.of_int pos in
Async_unix.Unix_syscalls.lseek fd ~mode:`Set posl
>>| fun _ ->
let wr = Writer.create t.fd in
let len = String.length s in
Writer.write wr s ~pos:0 ~len
任务需要是原子的,但事实并非如此,因为可以在第一个write
之前执行两个lseek
。即使我可以按顺序安排Writer.write
,但由于write
没有返回Writer.write
,因此无法提供帮助。有什么想法吗?
顺便说一句,这是之前回答question的后续行动。
答案 0 :(得分:2)
基本方法是拥有一个工作队列,每个工作人员执行原子seek/write
1 操作。不变量是一次只有一个工人在运行。更复杂的策略将采用优先级队列,其中写入由一些最大化吞吐量的标准排序,例如,写入后续位置。如果你观察到很多小写,你也可以实现复杂的缓冲策略,然后一个好主意就是将它们合并成更大的块。
但是,让我们从一个简单的非优先级队列开始,通过Async.Pipe.t
实现。对于位置写入,我们不能使用Writer接口,因为它是为缓冲顺序写入而设计的。因此,我们将使用来自Unix.lseek
和Bigstring.really_write Async_unix.Std
Fd.syscall_in_thread`函数的function. The really_write is a regular non-asynchronous function, so we need to lift it into the Async interface using the
,例如,
let really_pwrite fd offset bytes =
Unix.lseek fd offset ~mode:`Set >>= fun (_ : int64) ->
Fd.syscall_in_thread fd (fun desc ->
Bigstring.really_write desc bytes)
注意:此函数将写入系统决定的字节数,但不超过bytes
的长度。因此,您可能对实现将写入所有字节的really_pwrite
函数感兴趣。
整个方案将包括一个主线程,它将拥有一个文件描述符,并通过Async.Pipe接受来自多个客户端的写请求。假设每个写请求都是以下类型的消息:
type chunk = {
offset : int;
bytes : Bigstring.t;
}
然后您的主线程将如下所示:
let process_requests fd =
Async.Pipe.iter ~f:(fun {offset; bytes} ->
really_pwrite fd offset bytes)
really_pwrite
是一个真正写入所有字节并处理所有错误的函数。在实际执行Async.Pipe.iter'
系统调用之前,您还可以使用pwrite
函数并预先编程并合并写入。
还有一个优化说明。分配大字符串是一项相当昂贵的操作,因此您可以考虑预先分配一个大的大字符串并从中提供小块。这将创建有限的资源,因此您的客户将等到其他客户端完成其写入并释放其块。因此,您将拥有一个内存占用有限的受限制系统。
1)理想情况下我们应该使用pwrite
尽管Janestreet仅提供pwrite_assume_fd_is_nonblocking
函数,但在调用系统{{1}时不会释放OCaml运行时已完成,并将实际阻止整个系统。所以我们需要使用搜索和写入的组合。后者将释放OCaml运行时,以便程序的其余部分可以继续。 (另外,鉴于他们对非阻塞fd的定义,这个功能并没有多大意义,因为只有套接字和FIFO被认为是非阻塞的,据我所知,它们不支持搜索操作。我会在他们的bug跟踪器上提出问题。