这是错误的锁类型。flock
is only用于进程之间的锁定,而不是同一进程中的线程之间的锁定。从man 2 flock
:
A call to flock() may block if an incompatible lock is held by another
process. To make a nonblocking request, include LOCK_NB (by ORing)
with any of the above operations.
添加了强调。和...
A process may only hold one type of lock (shared or exclusive) on a
file. Subsequent flock() calls on an already locked file will convert
an existing lock to the new lock mode.
你想使用flockfile
相反(或者另外,如果也使用多个进程)。这flockfile
函数用于控制对FILE *
来自多个线程。从手册页:
The stdio functions are thread-safe. This is achieved by assigning to
each FILE object a lockcount and (if the lockcount is nonzero) an own‐
ing thread. For each library call, these functions wait until the FILE
object is no longer locked by a different thread, then lock it, do the
requested I/O, and unlock the object again.
(Note: this locking has nothing to do with the file locking done by
functions like flock(2) and lockf(3).)
像这样:
// in one of the threads...
flockfile(fp);
fwrite(..., fp);
funlockfile(fp);
好消息是glibc
,如果每个关键部分中只有一个来自 stdio.h 的函数调用,则不需要锁定文件,因为glibc
has a 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: flock successful
thread2: flock successful
thread2: exiting
thread1: exiting
done
注意线程1并没有释放flock
,并且线程 2 无论如何都能够获取它。使用条件变量可确保线程 1 在线程 2 获取锁之前不会退出。这正是flock
手册页说,因为flock
表示锁是每个文件和每个进程的,但不是每个线程的。
以原子方式追加到文件的摘要
为了在进程和线程之间进行原子写入,您可以执行以下两项简单操作之一:
Use write
并且写不超过PIPE_BUF
字节。PIPE_BUF
定义于<limits.h>
,在我的系统上是 4096。如果文件描述符在以下位置打开O_APPEND
模式,那么写入将自动到达文件末尾,无论其他人正在写入文件(线程和/或进程)。
Use write
and flock
。如果你ever写多于PIPE_BUF
一次字节,这是所有写入的唯一选择。同样,如果文件打开于O_APPEND
模式,那么字节将转到文件末尾。这将以原子方式发生,但仅从每个有能力的人的角度来看flock
.
此外,