c++ - mutex 或 flock fcntl.h 只锁定写操作

c++ - mutex or flock fcntl.h to lock only write operation

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

我在 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实现代码可以吗?

首先你需要澄清多线程与多进程:

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

(既然你说没有inter-process沟通,我建议走这条路。)

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

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

侧节点:

多线程编程的第一条规则不是'use a mutex',而是'try hard not to access data by multiple threads',甚至是'try hard not to use multiple threads, unless absolutely necessary for performance reasons'(例如,异步操作总是可以不用线程)

您可能不需要任何锁定。

使用 O_APPEND 标志集进行 open() 调用,正如@Jean-BaptisteYunès 在评论中提到的那样。

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

If the O_APPEND flag of the file status flags is set, the file offset shall be set to the end of the file prior to each write and no intervening file modification operation shall occur between changing the file offset and the write operation. [emphasis mine]

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

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

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

Is file append atomic in UNIX?

一定要阅读那里链接的问题。