c - 是否可以同时推送和弹出创建线程安全队列?
问题描述
这更像是一个理论而不是有用的问题,但我想不出办法来做到这一点。明确地说,目标是创建一个队列,允许多个线程同时推送和弹出而不会相互阻塞。
目标的一个示例是:线程 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。这是仅有的两个修改这些变量的函数。(我知道头/尾存在溢出的可能性,并且在数组中存储指针会使队列的线程安全变得不重要,但这些问题对于这个问题并不重要。)
问题的一个例子是:假设线程 A 和线程 B 调用push
并且线程 C 调用pop
。线程 A 获得插槽 0 但不锁定它,然后线程 B 获得插槽 1 并写入它并发布有写入的空间。线程 C 唤醒并获取插槽 0,并尝试从中读取,但线程 A 尚未写入。
我可以通过增加头/尾并在互斥锁中写入/读取来解决此问题,该互斥锁阻止任何其他线程访问整个队列,但我想知道是否有可能以允许多个线程的方式做到这一点同时写入和读取队列。
解决方案
这是我正在使用的解决方案:
void push(void* item) {
sem_wait(push_avaliability);
int slot = atomic_fetch_add(tail, 1) % queue_size;
while(slot_full[slot] != false);
queue[slot] = item;
slot_full[slot] = true;
sem_post(pop_avaliability);
}
void* pop() {
sem_wait(pop_avaliability);
int slot = atomic_fetch_add(head, 1) % queue_size;
while(slot_full[slot] != true);
void* item = queue[slot];
slot_full[slot] = false;
sem_post(push_avaliability);
return item;
}
(slot_full[i]
被初始化为假)
while 循环不会使用大量时间,因为信号量主要处理等待插槽打开的情况。(在测试中,while 循环条件永远不会为真。)如果复制操作很昂贵,那么使用条件变量将是可行的方法,但我认为平均而言这可能比这种解决方案更昂贵。
这篇论文是无锁队列的有用参考,以及 mpoeter 的评论。
推荐阅读
- caffeine - 咖啡因缓存 - 如何获取元素创建日期的信息
- sql-server - 访问递归公用表表达式 (CTE) 的递归部分中的当前行(不是前一行)
- c# - 如何在不同的函数中引用类对象?
- python - 为什么我不能对页面中的多个元素使用 WebDriver Wait for Selenium Webdriver?
- sql - 我在数据阶段工作,我正在尝试将数据从一列输入到另一列
- python - 如何从pygame构建可执行文件
- haskell - 连接函数的 Haskell 问题
- javascript - 如何使用带有钩子的 React Infinite Scroll
- spring-batch - 批量使用的弹簧云数据流是否只需要船长?
- html - 将 Google 日历添加到您的网站会使其不再是静态的吗?