Linux与FIFO等待队列的同步

6

Linux中是否有等待队列为FIFO的锁?这似乎是一个很明显的问题,但我刚刚发现pthread互斥锁不是FIFO,信号量似乎也不是FIFO(我正在2.4内核上工作(作业))...

Linux是否有带有FIFO等待队列的锁,或者有没有使用现有机制轻松实现它的方法?

3个回答

5

这是一种使用pthread原语构建简单队列式“票据锁”的方法。它应该可以给你一些想法:

#include <pthread.h>

typedef struct ticket_lock {
    pthread_cond_t cond;
    pthread_mutex_t mutex;
    unsigned long queue_head, queue_tail;
} ticket_lock_t;

#define TICKET_LOCK_INITIALIZER { PTHREAD_COND_INITIALIZER, PTHREAD_MUTEX_INITIALIZER }

void ticket_lock(ticket_lock_t *ticket)
{
    unsigned long queue_me;

    pthread_mutex_lock(&ticket->mutex);
    queue_me = ticket->queue_tail++;
    while (queue_me != ticket->queue_head)
    {
        pthread_cond_wait(&ticket->cond, &ticket->mutex);
    }
    pthread_mutex_unlock(&ticket->mutex);
}

void ticket_unlock(ticket_lock_t *ticket)
{
    pthread_mutex_lock(&ticket->mutex);
    ticket->queue_head++;
    pthread_cond_broadcast(&ticket->cond);
    pthread_mutex_unlock(&ticket->mutex);
}

4
如果你问的是我想象中的问题,简短的回答是否定的。线程/进程是由操作系统调度器控制的。随机的一个线程会获得锁,其他线程不会。如果你使用计数信号量,可能会有多个线程获得锁,但那可能不是你要问的。
你可能想看一下pthread_setschedparam,但这不会达到你想要去的地方。
你可以写点什么,但我怀疑它最终会变得低效,并且会打败使用线程的初衷,因为你只会将每个线程随机放弃,直到获得控制权的线程。
很有可能你只是从错误的角度思考问题。你可能需要描述一下你的目标,以便得到更好的建议。

我有一个服务端-客户端的情况,并且服务器需要以这样的方式保持日志文件,即写入其中不会干扰其I / O,这意味着服务器 - 客户端连接线程不能因为正在等待另一个锁定了日志文件的互斥量而去等待。我考虑将每个条目投入单独的“写入日志文件”线程中,该线程可以进入等待状态,而不会干扰其父线程。日志文件中的消息可以混合在一起,但必须按连接正确地维护时间顺序,这就是为什么我需要FIFO的原因。 - EpsilonVector
1
设置一个队列。c-s连接将其日志信息写入队列。单独的线程读取队列并写入文件。您仍然必须在队列上获取锁定,但除非您知道否则这应该是无关紧要的。获取队列锁定所需的时间将被相对缓慢的文件和网络I/O所淹没。由于任何给定的c-s线程都按顺序写入队列,因此每个线程的日志输出也将按顺序进行。 - Duck
是的,这正是我想要避免做的事情(需要比FIFIO锁更多的更改),但我想我已经没有选择了... - EpsilonVector

1
我最近有一个类似的需求,但需要处理多个进程。这是我找到的解决方案:
  • 如果您需要100%正确的FIFO排序,请选择caf的pthread ticket lock

  • 如果您对99%感到满意并且更喜欢简单性,则信号量或互斥锁实际上可以很好地完成。

票据锁可以跨进程使用:
您需要使用共享内存、进程共享互斥锁和条件变量,在互斥锁被锁定时处理进程死亡(->鲁棒互斥锁)...这有点过度,所有我需要的是不同的实例不同时调度并且顺序大致公平。

使用信号量:

static sem_t *sem = NULL;

void fifo_init()
{
    sem = sem_open("/server_fifo", O_CREAT, 0600, 1);
    if (sem == SEM_FAILED)  fail("sem_open");
}

void fifo_lock()
{
    int r;
    struct timespec ts;
    if (clock_gettime(CLOCK_REALTIME, &ts) == -1)  fail("clock_gettime");
    ts.tv_sec += 5;     /* 5s timeout */

    while ((r = sem_timedwait(sem, &ts)) == -1 && errno == EINTR)
        continue;       /* Restart if interrupted */
    if (r == 0)  return;

    if (errno == ETIMEDOUT) fprintf(stderr, "timeout ...\n");
    else                    fail("sem_timedwait");
}

void fifo_unlock()
{
    /* If we somehow end up with more than one token, don't increment the semaphore... */
    int val;
    if (sem_getvalue(sem, &val) == 0 && val <= 0)
        if (sem_post(sem))  fail("sem_post");
    usleep(1);  /* Yield to other processes */
}

订单几乎100%先进先出。

注意:这是在4.4 Linux内核下,2.4可能会有所不同。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接