2

我有这个工具,其中一个类似日志的文件由多个进程写入。

我想要实现的是在第一次打开文件时将其截断,然后由打开它的几个进程在最后完成所有写入。所有写入都被系统地刷新和互斥保护,这样我就不会得到混乱的输出。

首先,一个进程创建文件,然后启动一系列其他进程,一次一个,然后打开文件并写入文件(主进程有时会插入其他内容;从进程可能打开也可能不打开,并且写东西)。

我希望尽可能不使用更多已经存在的 IPC(我现在所做的只是写入一个 popen 创建的管道)。除了 CRT 和 Win32 API,我无法访问外部库,我不想开始编写序列化代码。

这是一些代码,显示了我去了哪里:

// open the file. Truncate it if we're the 'master', append to it if we're a 'slave'
std::ofstream blah(filename, ios::out | (isClient ? ios:app : 0));

// do stuff...

// write stuff
myMutex.acquire();
blah << "stuff to write" << std::flush;
myMutex.release();

好吧,这是行不通的:尽管从进程的输出按预期排序,但主进程写入的内容要么是捆绑在一起,要么是在错误的地方,如果它根本存在的话。

我有两个问题:给ofstream的构造函数的标志组合是正确的吗?反正我走对了吗?

4

6 回答 6

1

如果您要从多个线程将大量数据写入日志,则需要重新考虑设计,因为所有线程都会阻止尝试获取互斥锁,并且通常您不希望线程被阻止做工作,以便他们可以登录。在这种情况下,您需要编写工作线程以将条目记录到队列中(这只需要在内存中移动内容),并有一个专用线程将条目从队列中拉出并将它们写入输出。这样你的工作线程被阻塞的时间越短越好。

通过使用异步 I/O,您甚至可以做得更好,但这有点棘手。

于 2009-12-16T18:08:43.297 回答
1

正如 reinier 所建议的,问题不在于我使用文件的方式,而在于程序的行为方式。

fstreams 做得很好。

我错过的是主从之间的同步(前者假设一个特定的操作是同步的,而不是同步的)。

编辑:哦,好吧,打开标志仍然存在问题。使用 ios::out 打开文件的进程没有根据需要移动文件指针(擦除其他进程正在写入的文本),并且在写入 cout 时使用 seekp() 完全搞砸了输出,因为代码的另一部分使用 cerr。

我的最终解决方案是保留互斥锁和刷新,对于主进程,以 ios::out 模式打开文件(以创建或截断文件),关闭它并使用 ios::app 重新打开它。

于 2009-12-16T19:02:04.563 回答
1

我制作了一个'lil log 系统,它有自己的进程并会处理写入过程,这个想法很简单。使用日志的进程只是将它们发送到日志进程将尝试写入文件的待处理队列。这就像任何实时渲染应用程序中的批处理一样。这样您就可以摆脱过多的打开/关闭文件操作。如果可以,我将添加示例代码。

于 2009-12-23T13:24:19.733 回答
0

您如何创建该互斥锁?
为此,这需要一个命名的互斥锁,以便两个进程实际上锁定同一事物。
您可以使用一小段代码检查您的互斥锁是否确实正常工作,该代码将其锁定在一个进程和另一个尝试获取它的进程中。

于 2009-12-16T16:16:26.227 回答
0

我建议在释放互斥锁之前阻止文本完全写入文件。我曾经遇到过来自一个任务的文本被来自更高优先级线程的文本打断的情况;看起来不太漂亮。

此外,将格式设置为逗号分隔格式,或者一些可以轻松加载到电子表格中的格式。包括线程 ID 和时间戳。文本行的交错显示线程如何交互。ID 参数允许您按线程排序。时间戳可用于显示顺序访问以及持续时间。以电子表格友好格式编写将允许您使用外部工具分析日志文件,而无需编写任何转换实用程序。这对我帮助很大。

于 2009-12-16T19:03:15.343 回答
0

一种选择是使用ACE ::logging。它具有并发日志记录的有效实现。

于 2009-12-29T05:23:29.227 回答