c++链表实现的阻塞队列

最近从java源码里发现了阻塞队列的实现,觉得非常有趣。

首先,介绍下什么是阻塞队列。阻塞队列代表着一个队列可以线程安全的往该队列中写数据和从该队列中读数据。也就是说,我们可以在多个线程之间并发的进行写数据和读数据,而不会引发任何并发问题。

下面我们就说说如何实现一个阻塞队列。

而实现一个阻塞队列的前提:

  1. 需要能够使用链表实现一个队列
  2. 能够使用c++的锁机制,去给队列的写和读操作加锁。

为了性能,这里的读和写的锁不能是同一把锁。而对于一个链表队列来说,读取操作肯定需要涉及头指针,写操作肯定涉及尾指针。既然要实现读操作一把锁和写操作一把锁。那么就要求读操作只能更改头指针而不能更改尾指针,写操作只能更改尾指针而不能更改头指针。不满足这个要求,那么读写操作就不可能实现用两把锁分别对读写进行加锁。

基本队列的实现

现在我们先说说如何实现这个队列。

要求:入队操作(enqueue)只能操作尾指针(last), 出队操作(dequeue)只能操作头指针(head)。

对于队列的初始化,这里不能单纯的设置为空指针,需要将头尾指针同一节点。

下面我们来看入队操作如何实现

从这个入队操作来看,该操作只更改了尾指针last, 而没有更改头指针head。

其代码实现为:

    void enqueue(int item) {
Node *node = new Node(item);
last = last->next = node;
}

接下来我们来看出队操作如何实现

从出队操作来看就有趣的多,它抛弃了head所指向的节点,而这个节点有可能是那些节点呢?

  1. 初始化时所赋值的那个节点
  2. 出队后的节点

也就是说,head所指向的节点中的val值没有任何实际含义。当需要出队时,出队head指向的下一个节点first中val的值,然后抛弃head本身指向的值,让head指向head的下一个节点first,此时head原来所指向的节点将被删除。现在我们可以看出出队操作也只改变了头指针head的值。

其代码实现为:

    int dequeue() {
Node *h = head;
Node *first = head->next;
delete h;
head = first;
int x = first->item;
return x;
}

现在队列已经实现,下面就看看阻塞队列如何实现。

阻塞队列的实现

既然是阻塞队列,那就意味这加锁和等待。那就需要对c++的一些锁知识和条件变量有了解。

先来看看我们需要实现阻塞队列的那些方法:

Special Value Blocks Times out
Insert offer(o) put(o) offer(o,timeout)
Remove poll() take() poll(timeout)

入队

让我们先来实现put这个方法。

先看其实现流程图:

由于该队列实现有最大值限制,故在插入数据之前需要先判断该队列是否已满,已满则需等待该队列有可用空间。在该线程入队操作完成后,可能有别的线程也在等待入队,需要唤醒其他写数据的线程,使其继续执行后续操作。如果入队前队列为空,可能有出队操作正在阻塞等待读数,也需要去唤醒读数据的线程。

在看代码实现之前,我们需要定义一些变量用于代码实现环节:

    /* The capacity bound*/
int64_t capacity;
/*Current number of elements */
std::atomic<int64_t> count;
/** Lock held by take, pool, etc */
std::mutex takeLock;
/** Wait queue for waiting takes */
std::condition_variable notEmpty;
/** Lock held by put, offer, etc */
std::mutex putLock;
/** Wait queue for waiting puts */
std::condition_variable notFull;

put函数的代码实现:

void LinkedBlockingQueue::put(const int item){
int c;
{
std::unique_lock<std::mutex> lck{putLock};
if( count == capacity) {
notFull.wait(lck, [this](){return count < capacity;}); //(1)
}
enqueue(item); //不应该把申请空间放在锁里面,耗时有点大
c = count.fetch_add(1);
}
if(c + 1 < capacity) {
notFull.notify_one();
} if(0 == c) {
notEmpty.notify_one();
}
}

对于offer(o)的实现,主要更改是对上述代码(1)中的等待改为直接返回false, 表示,当前没有可用空间插入数据。正常插入就返回true.

对于offer(o,timeout)的实现,就需要在上述代码(1)中的wait函数添加上时间参数,使其可以在timeout时间内返回,如果是正常唤醒,正常入队,则返回ture,否则返回false.

该更改为:

            if(!notFull.wait_for(lck, rel_time, [this](){return count < capacity;})){
return false;
}

出队

对于出队,其实现和入队基本相同,基本上只需要更改其中的关键判断和通知。

take函数的代码实现为:

void LinkedBlockingQueue::take(int & returnVal) {
int c;
{
std::unique_lock<std::mutex> lck{takeLock};
if( 0 == count) {
notEmpty.wait(lck, [this](){return count > 0 ;});
}
returnVal = dequeue();
c = count.fetch_sub(1);
} if( c > 1 ) {
notEmpty.notify_one();
} if(c == capacity) {
notFull.notify_one();
}
}

剩下的实现细节可以参考我的实现

LinkBlockedQueue的c++实现的更多相关文章

  1. OO 第四单元总结

    一.总结本单元两次作业的框架设计 1.1. 需求分析 通过分析mdj文件可知,两次作业如果对于时间复杂度没有要求,可以不涉及任何数据结构,直接根据读入的UML_ELEMENT逐个分析得到各个函数的结果 ...

随机推荐

  1. Java线程阻塞方法sleep()和wait()精炼详解

    版权声明:因为个人水平有限,文章中可能会出现错误,如果你觉得有描述不当.代码错误等内容或者有更好的实现方式,欢迎在评论区告诉我,即刻回复!最后,欢迎关注博主!谢谢 https://blog.csdn. ...

  2. 中间件、蓝图、g对象

    中间件 ''' flask中一旦请求到来,要执行app()--->>>执行的是app.__call__,整个flask的入口 ''' from flask import Flask ...

  3. if-else​ 条件语句

    1.  条件语句模型 Go里的流程控制方法还是挺丰富,整理了下有如下这么多种: if - else 条件语句 switch - case 选择语句 for - range 循环语句 goto 无条件跳 ...

  4. 2020华为杯数学建模B题-RON建模 赛后总结与分析

    好久好久没有写博客了...挺累的,从二月份开始找暑期实习,接着在进行暑期实习,然后马不停蹄地进行秋招,现在总算结束实习,前两天又参加了华为杯数学建模竞赛,感觉接下来就会很轻松了,希望能好好休息休息.这 ...

  5. 容器云平台No.8~kubernetes负载均衡之ingress-nginx

    Ingress 是什么? Ingress 公开了从集群外部到集群内服务的 HTTP 和 HTTPS 路由. 流量路由由 Ingress 资源上定义的规则控制. 可以将 Ingress 配置为服务提供外 ...

  6. [SUCTF 2019]EasySQL1 及sql_mode

    (我被虐到了,呜呜呜) 当 sql_mode 设置了 PIPES_AS_CONCAT 时,|| 就是字符串连接符,相当于CONCAT() 函数 当 sql_mode 没有设置 PIPES_AS_CON ...

  7. IDEA2020.2的破解

    第一种方式:http://code.39sd.cn/ 直接获取二维码: 第二种:下载破解工具(本方法只是提供个人学习使用) 1.下载2020.2的idea 链接:https://pan.baidu.c ...

  8. NET Standard中配置TargetFrameworks输出多版本类库

    系列目录     [已更新最新开发文章,点击查看详细] 在.NET Standard/.NET Core技术出现之前,编写一个类库项目(暂且称为基础通用类库PA)且需要支持不同 .NET Framew ...

  9. RHSA-2018:0395-重要: 内核 安全和BUG修复更新(需要重启、本地提权、代码执行)

    [root@localhost ~]# cat /etc/redhat-release CentOS Linux release 7.2.1511 (Core) 修复命令: 使用root账号登陆She ...

  10. OracleOggan安装并测试同步数据步骤!

    Oracle Golden Gate (ogg)安装使用说明 Golden Gate(简称OGG)提供异构环境下交易数据的实时捕捉.变换.投递等功能. OGG支持的异构环境有: OGG的特性: ①对生 ...