【发布时间】:2020-11-08 02:26:25
【问题描述】:
这更像是一个理论而不是有用的问题,但我想不出办法来做到这一点。明确地说,目标是创建一个队列,允许多个线程同时推送和弹出,而不会相互阻塞。
目标的一个例子是:
线程 A 推送两次。线程 A 和线程 B 调用 push,线程 C 和线程 D 调用 pop。线程 A 获取队列位置 2,线程 B 获取队列位置 3,线程 C 获取队列位置 0,线程 D 获取队列位置 1。所有线程能够同时读取/写入各自的位置。
这些 push 和 pop 函数可以做到这一点,但不是线程安全的:
#include <pthreads.h>
#include <semaphore.h>
...
void push(void* item) {
sem_wait(push_avaliability);
int slot = atomic_fetch_add(tail, 1) % queue_size;
pthread_mutex_lock(access_queue[slot]);
queue[slot] = item;
pthread_mutex_unlock(access_queue[slot]);
sem_post(pop_avaliability);
}
void* pop() {
sem_wait(pop_avaliability);
int slot = atomic_fetch_add(head, 1) % queue_size;
pthread_mutex_lock(access_queue[slot]);
void* item = queue[slot];
pthread_mutex_unlock(access_queue[slot]);
sem_post(push_avaliability);
}
head和tail都被初始化为0,push_avaliability被初始化为队列的大小,pop_avaliability被初始化为0。这是仅有的两个修改这些变量的函数。 (我知道 head/tail 存在溢出的可能性,并且在数组中存储指针会使队列的线程安全变得不重要,但这些问题对于这个问题并不重要。)
问题的一个例子是:
假设线程 A 和线程 B 调用 push,线程 C 调用 pop。线程 A 获得 slot 0 但没有锁定它,然后线程 B 获得 slot 1 并写入它并发布有写入空间的帖子。线程 C 唤醒并获取 slot 0,并尝试从中读取,但线程 A 尚未写入。
我可以通过增加头/尾并在互斥锁中写入/读取来解决此问题,该互斥锁阻止任何其他线程访问整个队列,但我想知道是否有可能以允许多个线程的方式执行此操作线程同时写入和读取队列。
【问题讨论】:
-
AFAIK,不安全。一个可靠、高效的多生产者-消费者队列需要静音和信号量或 condvar(如果有界则更多)。
-
你应该看看无锁队列——最简单和最著名(虽然不是最好的)是Michael Scott queue。
标签: c multithreading