(Thread::Queue)队列数据结构(FIFO)是线程安全的,它保证了某些线程从一端写入数据,另一些线程从另一端读取数据。只要队列已经满了,写入操作就自动被阻塞直到有空间支持写操作,只要队列空了,读取操作就会自动阻塞直到队列中有数据可读。这种模式自身就保证了线程安全性。

创建队列

new()

new(LIST)

new()可以创建一个空队列,或者根据已有的列表创建队列,列表中的元素会按照先后顺序放进这个队列中。

哪些元素可放进队列

可以被threads::shared共享的数据都可以放进队列。包括:

  1. Ordinary scalars
  2. Array refs
  3. Hash refs
  4. Scalar refs
  5. Objects based on the above

放进队列的数据必须是已经共享的,如果没有共享,则会自动克隆(递归克隆)一份后将其共享并将共享后放进队列。

例如,下面首先会创建一个空队列,由于@arr未共享,所以会先通过&shared([])将一个空列表(匿名列表引用)共享,并将@arr中的3个元素放进去,然后再将这个共享的匿名列表放进队列。

  1. my @arr = qw(foo bar baz);
  2. $q->enqueue(\@arr);

但是下面的数据是已经共享的,将会直接放进队列中,而不需要先克隆一份。

  1. my @arr :shared = qw(foo bar baz);
  2. $q->enqueue(\@arr);

注意是整个列表引用,它将是队列中的单个元素,而不是将那三个元素放进队列。另外,需要克隆的时候,原始数据结构不会被共享,而是共享它的匿名结构的克隆。

操作队列的基本方法

enqueue(LIST)

将列表中的元素放进队列的尾部,给定的列表可以只有一个元素。默认情况下队列长度可以无限增长,但可以通过limit来设置

dequeue()

dequeue(COUNT)

从队列的头部移除COUNT(默认为1)个元素,并返回它们。如果队列中的元素数量少于请求移除的数量,则线程将被阻塞直到有足够的元素可返回

dequeue_nb()

dequeue_nb(COUNT)

以非阻塞的方式请求从队列头部移除COUNT(默认为1)个元素并返回它们,如果队列中元素个数少于请求移除的数量,则立即返回已存在的那些元素而不会阻塞等待,如果队列为空,则立即返回undef

dequque_timed(TIMEOUT)

dequque_timed(TIMEOUT, COUNT)

从队列的头部移除COUNT(默认为1)个元素,并返回它们。如果队列中的元素个数少于请求移除的个数,则阻塞直到有足够的元素可以返回或者阻塞直到超时。如果等待到了超时,则返回队列中已存在的元素,如果队列为空,则返回undef。如果省略了TIMEOUT或者定义为undef或小于等于0的值,则等价于dequeue_nb

timeout可以是以秒为单位计时的正数、小数(如4、2.5等),也可以是从1970-01-01 00:00:00距离现在已过去的秒数,即epoch时间戳。

pending()

返回队列中还有多少个元素,如果队列已被结束(end())了或已经没有元素了,则返回undef。

$QUEUE->limit = N

设置队列的最大长度为N,如果N=0则表示无限制。设置了limit后,后续的enqueue会被阻塞直到pending的数量小于limit的值,但是需要注意的是,在一次性向队列中enqueue多个元素导致跨了limit值时不会阻塞。见示例:

  1. my $q = Thread::Queue->new(1, 2);
  2. $q->limit = 4;
  3. $q->enquque(3,4,5); # 不会阻塞
  4. $q->enqueue(6); # 阻塞,直到队列中元素少于4
  5. my $size = $q->limit; # 返回当前的队列限制值,可能会返回undef
  6. $q->limit = 0; # 不再限制队列长度

end()

声明这个队列已经不会再有元素放进来了,也就是告诉对端要关闭队列,使其不要等待或阻塞。这也可以通过enqueue(undef)发送undef的方式来实现。

更多操作队列的方法

peek()

peek(INDEX)

从队列中返回指定索引位(注意:从队列头部开始计数)的元素但却不移除它,如果不提供INDEX则默认返回队列头部的元素(index=0)。INDEX可以是负数,-1表示队列的最尾部元素,-2表示尾部倒数第二个元素。

如果指定索引处元素不存在(即索引越界)或者队列为空,则返回undef。

记住,peek不会从队列中将数据移除掉,所以操作一个peek出来的数据可能会影响到队列中的对应元素。见后文示例。

insert(INDEX, LIST)

将list中的元素插入到INDEX(从队列头部计数)位置处。负数索引表示从队列尾部开始计数。如果指定的INDEX大于当前队列最大索引值,则直接插入到队列的尾部(正数INDE)或队列的头部(负数INDEX)。例如:

  1. $q->enqueue(1,2,3,4);
  2. $q->insert(1, qw(foo bar)); # 1, foo, bar, 2, 3, 4
  3. $q->enqueu(1, 2, 3, 4);
  4. $q->insert(-2, qw(foo bar)); # 1, 2, foo, bar, 3, 4

extract()

extract(INDEX)

extract(INDEX, COUNT)

以非阻塞的方式移除并返回指定索引位置处(从队列头部开始计数)指定个数的元素(默认为1个)。如果不给任何参数,则等价于dequeue_nb。如果元素个数不足,则能返回多少算多少。如果索引位置越界,则返回undef或空列表。

例如:

  1. $q->enqueue(1,2,3,4);
  2. my $item = $q->extract(2); # 3,队列包含:1 2 4
  3. my @items = $q->extract(1, 3); # 返回(2, 4),队列包含:1

由于peek方法可以直接从队列中获取元素但却不将其从队列中移除,这可能会导致队列中的引用类型元素在队列外被修改。例如,下面的代码:

  1. #!/usr/bin/perl
  2. use strict;
  3. use warnings;
  4. use threads;
  5. use threads::shared;
  6. use Thread::Queue;
  7. my $q = Thread::Queue->new();
  8. # 放非引用数据到队列
  9. $q->enqueue(1, 2, 3)
  10. # 放引用数据到队列
  11. $q->enqueue(['a', 'b', 'c'])
  12. my $thr = threads->new(
  13. sub {
  14. sleep 1;
  15. while(my $item = $q->dequeue()){
  16. # 如果是引用,要小心数据被其它线程修改
  17. if (ref $item){
  18. foreach (@$item){
  19. print "ele: $_\n";
  20. }
  21. } else {
  22. print "item: $item\n";
  23. }
  24. }
  25. }
  26. );
  27. my $num = $q->peek();
  28. $num = 11; # 不影响,因为是非引用数据
  29. my $list = $q->peek(3);
  30. $$list[1] = 'bb'; # 将影响队列中对应元素
  31. $q->end(); # 关闭队列
  32. $thr->join;

为了防止队列中的元素值被其它线程修改,可以使用threads::shared中的lock()将队列锁在一个代码块内。

  1. {
  2. lock($q);
  3. my $item = $q->peek();
  4. if($item...){
  5. ...
  6. }
  7. } # 释放锁

Perl线程队列:Thread::Queue的更多相关文章

  1. python线程+队列(queue)

    ---恢复内容开始--- python的线程学习 用处 pocpiliang脚本的编写 函数式:调用 _thread 模块中的start_new_thread()函数来产生新线程.语法如下: _thr ...

  2. Perl线程介绍

    Perl 中的线程 本文首先概述了线程的概念和意义,接着回顾了 Perl 语言中对线程支持的演化历史,然后通过一系列示例重点介绍了 Perl 中线程的实现,数据的共享以及线程间的同步,最后归纳并总结了 ...

  3. Python进阶----异步同步,阻塞非阻塞,线程池(进程池)的异步+回调机制实行并发, 线程队列(Queue, LifoQueue,PriorityQueue), 事件Event,线程的三个状态(就绪,挂起,运行) ,***协程概念,yield模拟并发(有缺陷),Greenlet模块(手动切换),Gevent(协程并发)

    Python进阶----异步同步,阻塞非阻塞,线程池(进程池)的异步+回调机制实行并发, 线程队列(Queue, LifoQueue,PriorityQueue), 事件Event,线程的三个状态(就 ...

  4. 同步异步,异步回调,线程队列,线程时间Event

    同步异步-阻塞非阻塞 阻塞-非阻塞 指的是程序的运行状态 阻塞:当程序执行过程中遇到了IO操作,在执行IO操作时,程序无法继续执行其他代码,称为阻塞. 非阻塞:程序在正常运行没有遇到IO操作,或者通过 ...

  5. 线程GIL锁 线程队列 回调函数

    ----------------------------------无法改变风向,可以调整风帆;无法左右天气,可以调整心情.如果事情无法改变,那就去改变观念. # # ---------------- ...

  6. day34 GIL锁 线程队列 线程池

    一.Gil锁(Global Interpreter Lock) python全局解释器锁,有了这个锁的存在,python解释器在同一时间内只能让一个进程中的一个线程去执行,这样python的多线程就无 ...

  7. C# 模拟一个处理消息队列的线程类 Message Queue

    // 模拟一个处理消息队列的类 class MessageHandler { // 消息队列 private Queue<string> messageQue = new Queue< ...

  8. 线程中的队列(queue)

    队列的类型和常用方法 队列是一种数据结构,它类似于列表.但列表是线程不安全的,而队列是线程安全的. python的queue(python3,python2为Queue)提供了3种队列: Queue: ...

  9. 网络编程基础--多线程---concurrent.futures 模块---事件Event---信号量Semaphore---定时器Timer---死锁现象 递归锁----线程队列queue

    1 concurrent.futures 模块: # from abc import abstractmethod,ABCMeta # # class A(metaclass=ABCMeta): # ...

随机推荐

  1. .NET题目(收集来自网络)

    1: .NET和c#有什么区别? 答: .NET一般是指.NET FrameWork框架,是一种平台,一种技术 c#是一种编程语言,是可以基于.NET平台的应用 2: c#中的委托是什么?事件是不是一 ...

  2. Spring Cloud 组件 —— feign

    feign 作为一个声明式的 Http Client 开源项目.在微服务领域,相比于传统的 apache httpclient 与在 spring 中较为活跃的 RestTemplate 更面向服务化 ...

  3. High Availability手册(1): 环境

    三台KVM虚拟机 首先我们得有一个pacemaker的环境,需要三台机器,如果没有那么多物理机器,可以用kvm虚拟机 创建一个bridge ovs-vsctl add-br ubuntu_br ifc ...

  4. QEMU KVM Libvirt(12): Live Migration

    由于KVM的架构为 Libvirt –> qemu –> KVM 所以对于live migration有两种方式,一种是qemu + KVM自己的方式,一种是libvirt的方式,当然li ...

  5. 【安富莱二代示波器教程】第17章 附件B---功能扩展和改进方向

    完整教程下载地址:http://forum.armfly.com/forum.php?mod=viewthread&tid=45785 第17章      附件B---功能扩展和改进方向   ...

  6. Java ActiveMQ 讲解(二)Spring ActiveMQ整合+注解消息监听

    对于ActiveMQ消息的发送,原声的api操作繁琐,而且如果不进行二次封装,打开关闭会话以及各种创建操作也是够够的了.那么,Spring提供了一个很方便的去收发消息的框架,spring jms.整合 ...

  7. 查看和修改Linux服务器的时区和时间

    一.查看和修改Linux的时区1. 查看当前时区 命令 : "date -R" 2. 修改设置Linux服务器时区方法 A 命令 : "tzselect" 方法 ...

  8. docker常用指令01

    1.进入交互式界面 docker run (--name=xx命名)-i -t ubuntu /bin/bash docker run --name=con1 -i -t ubuntu /bin/ba ...

  9. Spring介绍

    Spring介绍 Spring的核心是一个轻量级(Lightweight)的容器(Container),它是实现IoC(Inversion of Control)容器和非入侵性(No intrusiv ...

  10. 死磕 java集合之TreeMap源码分析(四)-内含彩蛋

    欢迎关注我的公众号"彤哥读源码",查看更多源码系列文章, 与彤哥一起畅游源码的海洋. 二叉树的遍历 我们知道二叉查找树的遍历有前序遍历.中序遍历.后序遍历. (1)前序遍历,先遍历 ...