Multithreading 轻量级多读/单写器队列实现
我需要一个非常简单的多读/单写器队列实现,用于一个小线程池(相当于物理内核的数量)。现在我用条件变量实现了它,但我正在为未来的版本探索更轻的替代方案。我以如下内容结束(C伪代码): 这个实现正确吗?故障在哪里?主操作系统(Linux、BSD、OSX、Windows)提供了哪些轻量级的等待信号/唤醒信号设备 编辑Multithreading 轻量级多读/单写器队列实现,multithreading,synchronization,message-queue,lock-free,Multithreading,Synchronization,Message Queue,Lock Free,我需要一个非常简单的多读/单写器队列实现,用于一个小线程池(相当于物理内核的数量)。现在我用条件变量实现了它,但我正在为未来的版本探索更轻的替代方案。我以如下内容结束(C伪代码): 这个实现正确吗?故障在哪里?主操作系统(Linux、BSD、OSX、Windows)提供了哪些轻量级的等待信号/唤醒信号设备 编辑 OS\u WAIT\u对象不强制为条件变量。这是一个简单的“进入睡眠状态,直到调度器唤醒您,因为其他人发送了一个信号”。条件变量的使用通常需要锁定(互斥)。因此,您的实现有一点意义(互斥
OS\u WAIT\u对象
不强制为条件变量。这是一个简单的“进入睡眠状态,直到调度器唤醒您,因为其他人发送了一个信号”。条件变量的使用通常需要锁定(互斥)。因此,您的实现有一点意义(互斥保护不需要CAS操作)。一些操作系统具有低级同步原语,这些原语位于互斥体和条件变量的下面,因此可以将它们直接用于SPMC队列。E、 g,Linux实现futex。@Tsyvarev我用互斥体和条件变量实现了我的工作队列。这不是我的工作队列,这是一个实验队列。OS_WAIT_对象不需要是条件变量。可以是操作系统可用的任何其他调度程序机制。简单地将操作系统等待对象解释为进入睡眠状态直到…
基本上是无用的:如果在检查条件后但在调用此函数之前改变了信号,您将等待条件为真。因此,给定的实现通常不可能是正确的。您需要具有更复杂语义的等待机制,但这种语义依赖于操作系统。@Tsyvarev如果这种检查和进入睡眠是在用户空间中完成的,是的,它是可以发生的,但如果它是在内核空间中完成的,我不明白为什么会发生。我有嵌入式编程的经验,我的推理是,这种机制可以通过HALT
和INT
机器指令实现,不会出现问题。操作系统调度器不能执行类似的任务吗?内核代码中没有检查和睡眠之间的竞争是由某种关键部分提供的。用户空间对应的临界区主要由互斥体实现,其中条件变量用于等待。这将返回到您的工作实现。那么,你到底想要什么?
#define QUEUE_MAXSIZE 16
struct queue {
OS_WAIT_OBJECT writer_waitobj;
OS_WAIT_OBJECT reader_waitobj;
size_t head;
size_t tail;
int data[QUEUE_MAXSIZE];
};
/* Called by a single writer. */
void enqueue(struct queue* queue, int value)
{
/* If queue is full wait to avoid overwriting. */
while (queue->head - queue->tail == QUEUE_MAXSIZE) {
OS_WAIT(queue->writer_waitobj);
}
/* There are a single writer so synchronization isn't necessary. */
queue->data[queue->head] = value;
queue->head++;
/* If queue was empty, signal the new state to one reader. */
if (queue->head == queue->tail) {
OS_AWAKE_ONE(queue->reader_waitobj);
}
}
/* Called by (few) multiple readers. */
int dequeue(struct queue* queue)
{
int value;
const size_t my_tail = tail;
/* If queue is empty readers must wait. */
while (queue->head == my_tail) {
OS_WAIT(queue->reader_waitobj)
}
/* Spin-lock readers synchronization. */
do {
value = queue->data[my_tail];
} while (!CPU_COMPARE_AND_SWAP(queue->tail, my_tail, my_tail+1));
/* If queue was full, signal the new state to writer. */
if (queue->head - my_tail == QUEUE_MAXSIZE) {
OS_AWAKE_ONE(queue->writer_waitobj);
}
return value;
}