c++ - 互斥锁或群 fcntl.h 锁定只写操作

Posted

技术标签:

【中文标题】c++ - 互斥锁或群 fcntl.h 锁定只写操作【英文标题】:c++ - mutex or flock fcntl.h to lock only write operation 【发布时间】:2017-07-01 10:18:18 【问题描述】:

我正在尝试从不同的线程(类似于日志记录)将文件附加(写入附加),因此不需要进程间锁定。

我在 fcntl.h 中研究了flock,它说flock 可以与进程间一起进行粒度锁定,这在我的情况下是不必要的。

char* file = "newfile.txt";
int fd;
struct flock lock;

printf("opening %s\n", file);
fd = open(file, O_APPEND);
if (fd >= 0) 
    memset(&lock, 0, sizeof (lock));
    lock.l_type = F_WRLCK;
    fcntl(fd, F_SETLKW, &lock);
    //do my thing here
    lock.l_type = F_UNLCK;
    fcntl(fd, F_SETLKW, &lock);
    close(fd);

会不会有开销,因为它可以进行粒度锁定和进程间锁定?有锁时程序崩溃怎么办?

我目前的偏好是互斥体,

static std::mutex fileMutex;
fileMutex.lock();
//do my thing here    
fileMutex.unlock();

是否可以使用互斥锁方法,因为仅在进程内需要同步(或锁定)(仅多线程),

或者用fcntl.h中的flock实现代码可以吗?

【问题讨论】:

以 O_APPEND 模式打开! 【参考方案1】:

您可能不需要任何锁定。

使用 @Jean-BaptisteYunès 在 cmets 中提到的 O_APPEND 标志设置您的 open() 呼叫。

然后使用 single write() 调用写入您的数据。 POSIX 保证如果文件以附加模式打开,单个 write() 操作将是原子的。 Per the POSIX standard:

如果设置了文件状态标志的 O_APPEND 标志,则文件偏移量 应在每次写入之前设置为文件末尾,并且 no 干预文件修改操作应在更改之间发生 文件偏移量和写入操作。 [强调我的]

您唯一的问题是如何处理部分write() - 其中单个write() 操作不会写入所有请求的数据。该标准要求每个write() 操作都是原子的——它不保证写入34 MB 的请求将导致写入整个34 MB。根据我的经验,在write() 调用请求移动大量字节之前,对实际文件的部分write() 调用根本不会发生——我从未观察到任何单个IO 操作上的部分write() 结果文件 小于 1 MB - 我已经为很多大型组织完成了 SAN 安装和基准测试。

因此,如果您将write() 调用限制在PIPE_BUF 或更少字节(在Linux 上),您几乎可以肯定可以避免所有锁定并让内核内部的内部锁定解决您的问题。

更多详情,请参阅以下内容:

Is file append atomic in UNIX?

请务必阅读此处链接的问题。

【讨论】:

如果写入超过PIPE_BUF,大小很容易超过。我现在只能想到带有 ofstream 写入的互斥锁。 @fury.slay 因为你在 Linux 上运行,如果你控制你的整个平台——包括你将要写入的文件系统——你将不得不测试并找出什么实际限制是。请参阅***.com/questions/10650861 如果您使用的是 Solaris 或 AIX,则几乎可以肯定,每次写入操作最多可以安全超过 1 MB(请注意,在 ZFS 上的链接中 - 来自 Solaris - 每个 write() 都是原子的......)更高版本的 Linux,您可能是。 我可以在我的系统上测试,但是应用程序是捆绑在用户不同类型的linux操作系统上运行的,所以我对安全性不做任何保证。如果我错了,请纠正我。【参考方案2】:

首先你需要明确多线程与多进程:

多线程:我建议使用互斥锁。您还可以通过将日志消息添加到内存缓冲区的末尾来提高效率,并且您只使用互斥锁保护对该缓冲区的访问。然后,您可以让另一个线程或某些常规维护功能将缓冲区刷新到文件中,而不会在 I/O 进行时锁定其他线程,也不会锁定文件。对文件的访问既不会受到互斥锁的保护,也不会受到文件锁的保护,因为它只能由单个线程访问。

(既然你说没有进程间通信,我建议走这条路。)

多进程:您必须使用一些所有进程都可见的锁定机制,例如您建议的文件锁。

两者:同时使用这两种机制。尝试仅在绝对最短的时间内锁定文件。

侧节点:

多线程编程的第一条规则不是“使用互斥锁”,而是“尽量不让多线程访问数据”,甚至是“尽量不使用多线程,除非出于性能原因绝对必要”(例如,对于异步操作,您总是可以不使用线程)。

【讨论】:

我已经提到没有进程间通信(没有多进程) 所以你建议在多线程互斥的情况下比flock好得多,不需要flock,不是吗? @fury.slay:是的,当然。您可以在文件操作周围放置一个互斥锁。这在功能上是足够和正确的,但速度很慢。所以我建议只从一个特定线程访问文件(因此文件 I/O 不需要互斥锁),然后使用保护共享内存缓冲区(字符串或字节队列或任何你拥有的)一个互斥体。

以上是关于c++ - 互斥锁或群 fcntl.h 锁定只写操作的主要内容,如果未能解决你的问题,请参考以下文章

fcntl和flock两个系统调用的区别

哪个更有效,基本互斥锁或原子整数?

const 成员函数中的互斥锁或原子

fcntl.h的表头文件

在优先级反转问题上,我们应该更改互斥锁或线程的属性吗?

C++ Pthread 互斥锁