那是一种错误的锁。 flock 仅用于进程之间的锁定,而不是同一进程中的线程之间的锁定。来自man 2 flock:
如果
other 持有不兼容的锁,则调用 flock() 可能会阻塞
进程。 要发出非阻塞请求,请包含 LOCK_NB(通过 ORing)
进行上述任何操作。
添加了重点。还有……
一个进程只能持有一种类型的锁(共享或独占)
文件。
对已锁定文件的后续flock() 调用将转换
一个现有的锁到新的锁模式。
您想改用flockfile(或另外,如果还使用多个进程)。 flockfile 函数用于控制从多个线程对FILE * 的访问。从手册页:
stdio 函数是线程安全的。这是通过分配给
每个 FILE 对象都有一个 lockcount 和(如果 lockcount 非零)一个 own-
荷兰国际集团线程。对于每个库调用,这些函数都会等到 FILE
对象不再被其他线程锁定,然后锁定它,执行
请求 I/O,然后再次解锁对象。
(注意:此锁定与文件锁定无关
像flock(2) 和lockf(3) 这样的函数。)
像这样:
// in one of the threads...
flockfile(fp);
fwrite(..., fp);
funlockfile(fp);
好消息是,在glibc 上,如果每个关键部分中只有一个来自 stdio.h 的函数调用,则不需要锁定文件,因为 glibc 有一个锁定的 fwrite。但在其他平台上情况并非如此,锁定文件当然没有坏处。因此,如果您在 Linux 上运行,您永远不会注意到 flock 不会做您想做的事情,因为 fwrite 会自动完成。
关于追加模式: 使用追加模式编写时不需要额外的刷新,除非你想确保打开相同文件的不同进程之间的顺序(或者一个进程具有多个句柄)同一个文件)。除非从文件中读取,否则不需要“a+”模式。
flock的演示
如果您不相信flock 不提供使用相同文件描述符的线程之间的线程安全,这里有一个演示程序。
#include <stdio.h>
#include <errno.h>
#include <pthread.h>
#include <string.h>
#include <stdlib.h>
#include <sys/file.h>
static FILE *fp;
static pthread_mutex_t mutex;
static pthread_cond_t cond;
int state;
static void fail_func(int code, const char *func, int line)
{
fprintf(stderr, "%s:%d: error: %s\n", func, line, strerror(code));
exit(1);
}
#define fail(code) fail_func(code, __FUNCTION__, __LINE__)
void *thread1(void *p)
{
int r;
// Lock file (thread 2 does not have lock yet)
r = pthread_mutex_lock(&mutex);
if (r) fail(r);
r = flock(fileno(fp), LOCK_EX);
if (r) fail(errno);
puts("thread1: flock successful");
state = 1;
r = pthread_mutex_unlock(&mutex);
if (r) fail(r);
// Wake thread 2
r = pthread_cond_signal(&cond);
if (r) fail(r);
// Wait for thread 2
r = pthread_mutex_lock(&mutex);
if (r) fail(r);
while (state != 2) {
r = pthread_cond_wait(&cond, &mutex);
if (r) fail(r);
}
puts("thread1: exiting");
r = pthread_mutex_unlock(&mutex);
if (r) fail(r);
return NULL;
}
void *thread2(void *p)
{
int r;
// Wait for thread 1
r = pthread_mutex_lock(&mutex);
if (r) fail(r);
while (state != 1) {
r = pthread_cond_wait(&cond, &mutex);
if (r) fail(r);
}
// Also lock file (thread 1 already has lock)
r = flock(fileno(fp), LOCK_EX);
if (r) fail(r);
puts("thread2: flock successful");
// Wake thread 1
state = 2;
puts("thread2: exiting");
r = pthread_mutex_unlock(&mutex);
if (r) fail(r);
r = pthread_cond_signal(&cond);
if (r) fail(r);
return NULL;
}
int main(int argc, char *argv[])
{
pthread_t t1, t2;
void *ret;
int r;
r = pthread_mutex_init(&mutex, NULL);
if (r) fail(r);
r = pthread_cond_init(&cond, NULL);
if (r) fail(r);
fp = fopen("flockfile.txt", "a");
if (!fp) fail(errno);
r = pthread_create(&t1, NULL, thread1, NULL);
if (r) fail(r);
r = pthread_create(&t2, NULL, thread2, NULL);
if (r) fail(r);
r = pthread_join(t1, &ret);
if (r) fail(r);
r = pthread_join(t2, &ret);
if (r) fail(r);
puts("done");
return 0;
}
在我的系统上,它产生以下输出:
thread1: 集群成功
线程2:集群成功
线程2:退出
线程1:退出
完毕
请注意,线程 1 不会释放 flock,而线程 2 无论如何都可以获取它。使用条件变量可确保线程 1 在线程 2 获得锁之前不会退出。 这正是 flock 手册页所说的, 因为 flock 说锁是每个文件和每个进程的,而不是每个线程的。
以原子方式附加到文件的摘要
为了在进程和线程之间进行原子写入,您可以做以下两种简单的事情之一:
使用write 并且写入不超过PIPE_BUF 字节。 PIPE_BUF 定义在 <limits.h> 中,在我的系统上是 4096。如果文件描述符在 O_APPEND 模式下打开,那么写入将自动到文件末尾,无论还有谁在写入文件(线程和/或进程)。
使用write 和flock。如果您曾经一次写入超过PIPE_BUF 个字节,这是您所有写入的唯一选择。同样,如果文件以O_APPEND 模式打开,则字节将转到文件末尾。这将自动发生,但仅从拥有flock 的每个人的角度来看。
另外,