0

我有一个多线程进程,其中一个文件由多个线程共享(读取和写入)。有什么方法可以让一个线程锁定一个文件段,使其他线程无法访问它?我试过fcntl(fd, F_SETLKW, &flock)了,但是这个锁只适用于进程,而不是线程(进程中的所有线程共享一个锁)。

4

4 回答 4

2

是的 - 但不是使用相同的机制。您必须使用诸如 pthread 互斥锁之类的东西,并自己跟踪簿记。

如何完成这项工作的可能大纲

  • 等待并通过簿记结构声明进程级互斥锁
    • 确保您的进程中没有其他线程正在尝试使用该段
    • 将自己标记为使用文件段
  • 释放进程级互斥锁

  • 获取进程的 fnctl 锁(如有必要)

  • 做你的写作
  • 释放 fnctl 锁以允许其他进程使用该段(如果需要)

  • 在进程级簿记结构互斥体上再次等待(如果您可以原子地标记它未使用,则可能没有必要)

    • 在您的进程中将段标记为未使用。
  • 释放进程级互斥锁
于 2012-10-20T23:55:55.070 回答
1

不。您询问的区域锁定功能具有令人惊讶的语义,并且由于它由 POSIX 控制,因此不会进一步开发。(事实上​​,这是 Kirk McKusick 对 POSIX 问题的首选示例。)如果 Linux 中有非 POSIX 字节范围锁定工具,我找不到它。

这里讨论了多线程世界中 POSIX 字节范围锁定的问题:http: //www.samba.org/samba/news/articles/low_point/tale_two_stds_os2.html

但是,如果您只关心一个进程中的线程,您可以使用信号量构建您自己的区域锁定。例如:

#include <stdbool.h>
#include <pthread.h>
#include <sys/types.h>

// A record indicating an active lock.
struct threadlock {
  int fd;  // or -1 for unused entries.
  off_t start;
  off_t length;
};

// A table of all active locks (and the unused entries).
static struct threadlock all_locks[100];

// Mutex housekeeping.
static pthread_mutex_t mutex;
static pthread_cond_t some_lock_released;
static pthread_once_t once_control = PTHREAD_ONCE_INIT;
static void threadlock_init(void) {
  for (int i = 0; i < sizeof(all_locks)/sizeof(all_locks[0]); ++i)
    all_locks[i].fd = -1;
  pthread_mutex_init(&mutex, (pthread_mutexattr_t *)0);
  pthread_cond_init(&some_lock_released, (pthread_condattr_t *)0);
}

// True iff the given region overlaps one that is already locked.
static bool region_overlaps_lock(int fd, off_t start, off_t length) {
  for (int i = 0; i < sizeof(all_locks)/sizeof(all_locks[0]); ++i) {
    const struct threadlock *t = &all_locks[i];
    if (t->fd == fd &&
        t->start < start + length &&
        start < t->start + t->length)
      return true;
  }
  return false;
}

// Returns a pointer to an unused entry, or NULL if there isn't one.
static struct threadlock *find_unused_entry(void) {
  for (int i = 0; i < sizeof(all_locks)/sizeof(all_locks[0]); ++i) {
    if (-1 == all_locks[i].fd)
      return &all_locks[i];
  }
  return 0;
}

// True iff the lock table is full.
static inline bool too_many_locks(void) {
  return 0 == find_unused_entry();
}

// Wait until no thread has a lock for the given region
// [start, start+end) of the given file descriptor, and then lock
// the region. Keep the return value for threadunlock.
// Warning: if you open two file descriptors on the same file
// (including hard links to the same file), this function will fail
// to notice that they're the same file, and it will happily hand out
// two locks for the same region.
struct threadlock *threadlock(int fd, off_t start, off_t length) {
  pthread_once(&once_control, &threadlock_init);
  pthread_mutex_lock(&mutex);

  while (region_overlaps_lock(fd, start, length) || too_many_locks())
    pthread_cond_wait(&some_lock_released, &mutex);

  struct threadlock *newlock = find_unused_entry();
  newlock->fd = fd;
  newlock->start = start;
  newlock->length = length;

  pthread_mutex_unlock(&mutex);
  return newlock;
}

// Unlocks a region locked by threadlock.
void threadunlock(struct threadlock *what_threadlock_returned) {
  pthread_mutex_lock(&mutex);

  what_threadlock_returned->fd = -1;
  pthread_cond_broadcast(&some_lock_released);

  pthread_mutex_unlock(&mutex);
}

注意:代码可以编译,但我还没有对它进行一点测试。

于 2012-10-20T20:24:29.290 回答
0

如果您不需要不同进程之间的文件锁,请避免使用文件锁(这是 POSIX API 设计最糟糕的部分之一),而只需使用互斥锁或其他共享内存并发原语。

于 2012-10-20T19:02:51.977 回答
0

有两种方法可以做到:

  1. 使用 Mutex 在同一进程内的线程中获取记录的锁定。一旦获得锁,进程中的任何其他线程,映射试图获得锁的文件都会被阻塞,直到锁被释放。(Linux 中最好的也是唯一最直接的解决方案)。

  2. 共享内存或内存映射文件上的信号量和互斥锁。

于 2012-10-20T19:29:42.540 回答