如何实现平台无关的异步写入文件?

Posted

技术标签:

【中文标题】如何实现平台无关的异步写入文件?【英文标题】:How to implement platform independent asynchronous write to file? 【发布时间】:2012-11-16 16:15:55 【问题描述】:

我正在创建一个程序,该程序将从远程机器接收消息,并且需要将消息写入磁盘上的文件。我发现的困难在于这个程序的目的是测试接收消息的库的性能,因此,我需要确保将消息写入磁盘不会影响库的性能.该库通过回调函数将消息传递给程序。另一个困难是解决方案必须独立于平台。

我有什么选择?

我想到了以下几点:

使用boost:asio 写入文件,但似乎(请参阅this 文档)异步写入文件位于此库的Windows 特定部分 - 因此无法使用。 使用boost::interprocess创建消息队列,但this documentation表示有3种方法可以发送消息,如果消息队列已满,所有方法都需要程序阻塞(隐式或不阻塞) ,我不能冒险。 创建一个std::deque<MESSAGES> 以从回调函数推送到双端队列,并在写入文件时弹出消息(在单独的线程上),但STL 容器是not guaranteed to be thread-safe。我可以锁定双端队列的推送和弹出,但我们谈论的是连续消息之间的 47 微秒,所以我想完全避免锁定。

有人对可能的解决方案有更多想法吗?

【问题讨论】:

47micros 在哪些消息之间?是远程机器节奏吗? 【参考方案1】:

STL 容器可能不是线程安全的,但我从未遇到过不能在不同时间在不同线程上使用的容器。将所有权传递给另一个线程似乎是安全的。

我已经使用了几次,所以我知道它有效:

创建一个指向 std::vector 的指针。 创建一个互斥锁来保护向量指针。 使用 new[] 创建一个 std::vector,然后为它保留一个大尺寸。

在接收线程中:

在向队列中添加项目时锁定互斥锁。这应该是一个短锁。 添加队列项。 释放锁。 如果你觉得它是一个条件变量的信号。我有时不这样做:这取决于设计。如果音量非常高并且接收端没有暂停,则跳过该条件并改为轮询。

在消费者线程(磁盘写入器)上:

通过轮询或等待条件变量来寻找工作: 锁定队列互斥体。 查看队列长度。 如果队列中有工作,则将指针分配给消费者线程中的变量。 使用 new[] 和 reserve() 创建一个新的队列向量并将其分配给队列指针。 解锁互斥锁。 开始将项目写入磁盘。 delete[] 已用完的队列向量。

现在,根据您的问题,您最终可能需要一种阻止方法。例如,在我的一个程序中,如果队列长度达到 100,000 个项目,则生产线程刚刚开始进行 1 秒的睡眠并抱怨很多。这是不应该发生的事情之一,但确实发生了,所以你应该考虑一下。完全没有任何限制,它只会使用机器上的所有内存,然后出现异常崩溃、被 OOM 杀死或在交换风暴中停止。

【讨论】:

我刚刚阅读了另一个答案。如果每个项目的锁定成本太高,您也可以根据该要求进行调整。只保留一个向量指针向量,写完后才添加一个向量。 另一个注意事项:这种设计在 Linux 上对我来说非常有效,其中互斥锁使用 futexes,在没有争用的情况下成本非常低。在 BSD 或 Windows 上使用 CriticalSection 也不错。但既然你想成为非常多平台的嵌套容器锁定可能真的是最好的。【参考方案2】:

boost::thread 独立于平台,因此您应该能够利用它创建一个线程来执行阻塞写入。为了避免每次将消息放入主线程时都需要锁定容器,您可以通过创建嵌套容器来利用双缓冲技术的修改,例如:

std::deque<std::deque<MESSAGES> >

然后,只有在准备好添加满是消息的双端队列时才锁定***双端队列。写入线程将依次仅锁定***双端队列以弹出一个充满要写入的消息的双端队列。

【讨论】:

以上是关于如何实现平台无关的异步写入文件?的主要内容,如果未能解决你的问题,请参考以下文章

异步写入 R 中的文件

java客户端如何向服务器txt文件写入信息

以与平台无关的方式将行添加到文件中

如何使用 Parallel.ForEach 正确写入文件?

Node.js学习之路05——fs文件系统之文件的写入和读取

15.swoole学习笔记--异步写入文件