我正在尝试从不同线程向文件追加(写追加)(类似于日志记录),因此不需要进程间锁定。
我在fcntl.h中研究过羊群,它说羊群可以在中间过程中进行颗粒锁定,这在我的情况下是不必要的。
char* file = "newfile.txt";
int fd;
struct flock lock;
printf("opening %sn", 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实现代码可以吗?
您可能不需要任何锁定。
正如@Jean-BaptisteYunès在评论中提到的那样,在设置O_APPEND
旗帜的情况下进行open()
通话。
然后使用单个write()
调用写入数据。POSIX保证,如果以追加模式打开文件,则单个write()
操作将是原子操作。根据POSIX标准:
如果设置了文件状态标志的O_APPEND标志,则文件偏移量应在每次写入之前设置到文件末尾,并且否在更改之间应进行中间文件修改操作文件偏移量和写入操作[强调矿]
您唯一的问题是如何处理部分write()
,其中单个write()
操作不会写入所有请求的数据。该标准要求每个write()
操作都是原子操作——它不能保证写入34MB的请求会导致写入整个34MB。根据我的经验,在write()
调用请求移动大量字节之前,对实际文件的部分write()
调用根本不会发生——我从未在1 MB以下的文件上的任何单个IO操作上观察到部分write()
结果,而且我已经为相当多的大型组织进行了SAN安装和基准测试。
因此,如果您将write()
调用限制在PIPE_BUF
或更少字节(在Linux上),那么几乎可以肯定地避免所有锁定,并让内核内部的锁定来解决您的问题。
有关更多详细信息,请参阅以下内容:
文件追加在UNIX中是原子的吗?
请务必阅读从那里链接的问题。
首先,您需要澄清多个线程与多个进程:
多线程:我建议使用互斥锁。您还可以通过将日志消息添加到内存缓冲区的末尾来提高效率,并且只使用互斥锁来保护对该缓冲区的访问。然后,您可以让另一个线程或一些常规维护函数将缓冲区刷新到文件中,而无需在I/O进行时锁定其他线程,也无需锁定文件。对文件的访问既不受互斥锁的保护,也不受文件锁的保护。因为只有一个线程才能访问每个文件。
(由于您表示没有进程间通信,我建议采用这种方式。)
多个进程:您必须使用某些所有进程都可见的锁定机制,例如您建议的文件锁定。
两者:同时使用这两种机制。请尝试仅在绝对最短的时间内锁定文件。
侧节点:
多线程编程的第一条规则不是"使用互斥",而是"尽量不让多个线程访问数据",甚至"尽量不使用多个线程,除非出于性能原因绝对必要"(例如,异步操作总是可以不使用线程)。