在使用Queue模块+多线程模拟生产者+消费者问题时,遇到了一个小问题,现在记录下来。供可能会遇到类似问题的初学者们参考。

该问题的完整参考代码如下。主要实现了以下的功能:在一个线程中,开启生产者模式,生成出来的object会put进一个Queue对象queue中。除此以外,在n个线程中(本代码中n为5,nfuncs = 5),开启消费者模式,每一个消费者线程会在一个while循环中不断地从queue中消耗一个object(使用get方法),直到生产者生产出的object被全部消耗(本代码中设为100个object)。

from random import randint
from time import sleep
from Queue import Queue
from threading import Thread,Lock,currentThread
lock = Lock()

def writes(queue):
print "producing object for Q...",
queue.put('xxx', 1)
print "size now", queue.qsize()

def readQ(queue):
val = queue.get(1)
print "consumed object from Q... size now", \
queue.qsize()
print currentThread().name

def writer(queue, loops):
for i in range(loops):
lock.acquire()
writes(queue)
lock.release()
sleep(0.1)

def read(queue):
while queue.qsize() >0 :
sleep(randint(2, 4))
lock.acquire()
readQ(queue)
lock.release()

funcs = [writer, read]
nfuncs = range(5)

def main():
nloops1 = 100
q = Queue(1024)

thread = []
t = Thread(target = writer, args = (q, nloops1))
thread.append(t)

for i in nfuncs:
t = Thread(target = read, args = (q,))
thread.append(t)

for i in range(len(thread)):
thread[i].start()

for i in range(len(thread)):
thread[i].join()

print "All Done"

if __name__ == '__main__':
main()

为了防止生产速度跟不上消费的速度,生产线程中每次生产仅间隔0.1秒,且在消费线程中每次消费之前,随机sleep 2~3秒。
在运行之后,生产的object数量达到100(实际不会print 100号object的生成,因为在生产的过程中已经开始消费),然后多个线程开始消费。然而在把object数量消费至0以后,线程们并不会结束,既“print “”All done“””语句一直没有被执行。

思考以后,得出了三种解决途径:

1.在线程的Join方法中加入参数timeout,如果线程阻塞,线程运行时间达到timeout时,将中止该线程。

该方法的缺点在于当生产数量不确定时,timeout的时间无法很好的确定。如果join的时间太短,可能有的进程还在运行,主进程就继续运行了。如果join的时间太长,在线程很多的情况下,将会阻塞很长的一段时间。

2.试图考虑为什么线程会阻塞。

发现在read函数中,如前所述,在每次消费之前,随机sleep 2~3秒。于是可能会出现以下的问题。

当 queue.qsize() = 1的时候,某个线程x进入了while循环,然后开始睡眠2~3秒,在这个睡眠过程中,GUI切换至其它的线程,此时由于线程x处于睡眠,并没有调用readq函数,因此queue中仍然有一个元素。以此类推,每个进程都在queue.qsize() = 1时进入了while循环,然后最早结束睡眠的线程将调用readq函数中的queue.get(1)。之后其它进程在调用queue.get(1)时,将会因为queue中缺少元素阻塞。

解决方法如下:

def read(queue):
sleep(randint(2, 4))
while queue.qsize():
lock.acquire()
readQ(queue)
lock.release()
sleep(randint(2, 4))

在while之前睡眠,且将每次消费时所需的睡眠放至readQ函数之后。可避免多个线程同时进入while循环,该改进将不会

引起阻塞。

3.Queue中的get方法,若将其参数设置为非0,则不会因为队列中没有可用元素而阻塞。将get的参数设置为0,

利用try/excep语句,当get不到数据引起异常时,excep一个break,中断线程。

Python中多线程的阻塞问题的更多相关文章

  1. 通过编写聊天程序来熟悉python中多线程及socket的用法

    1.引言 Python中提供了丰富的开源库,方便开发者快速就搭建好自己所需要的应用程序.本文通过编写基于tcp/ip协议的通信程序来熟悉python中socket以及多线程的使用. 2.python中 ...

  2. Python 中多线程之 _thread

    _thread模块是python 中多线程操作的一种模块方式,主要的原理是派生出多线程,然后给线程加锁,当线程结束的 时候取消锁,然后执行主程序 thread 模块和锁对象的说明 start_new_ ...

  3. python中多线程相关

    基础知识 进程:进程就是一个程序在一个数据集上的一次动态执行过程 数据集:程序执行过程中需要的资源 进程控制块:完成状态保存的单元 线程:线程是寄托在进程之上,为了提高系统的并发性 线程是进程的实体 ...

  4. Python中多线程与多进程的恩恩怨怨

    概念: 并发:当有多个线程在操作时,如果系统只有一个CPU,则它根本不可能真正同时进行一个以上的线程,它只能把CPU运行时间划分成若干个时间段,再将时间 段分配给各个线程执行,在一个时间段的线程代码运 ...

  5. Python 中多线程共享全局变量的问题

    写在前面不得不看的一些P话: Python 中多个线程之间是可以共享全局变量的数据的. 但是,多线程共享全局变量是会出问题的. 假设两个线程 t1 和 t2 都要对全局变量g_num (默认是0)进行 ...

  6. python中多线程

    多线程 什么是多线程 开启线程的两种方式 进程和线程的区别 Thread对象的其他属性和方法 守护线程 死锁现象与递归锁 信号量.Event定时器 线程Queue 进程池和线程池 什么是多线程 在传统 ...

  7. python中多线程,多进程,多协程概念及编程上的应用

    1, 多线程 线程是进程的一个实体,是CPU进行调度的最小单位,他是比进程更小能独立运行的基本单位. 线程基本不拥有系统资源,只占用一点运行中的资源(如程序计数器,一组寄存器和栈),但是它可以与同属于 ...

  8. python中多线程(1)

    一多线程的概念介绍 threading模块介绍 threading模块和multiprocessing模块在使用层面,有很大的相似性. 二.开启多线程的两种方式 1.创建线程的开销比创建进程的开销小, ...

  9. python中多线程,多进程,队列笔记(一)

    threading简介:If you want your application to make better use of the computational resources of multi- ...

随机推荐

  1. VScode 常用快捷键 2019

    窗口操作 Ctrl + b      : 显示/隐藏左侧工作区文件目录 View   Appearance   show Activity bar : 最左侧工具栏 显示/隐藏 Preferences ...

  2. 将IDEA工程代码提交到Github

    1.git安装配置 1.下载git https://git-scm.com/download/win 2.安装 傻瓜式安装即可,记住安装的目录 3.配置 2.配置git SSH 1.首先申请一个Git ...

  3. knn算法手写字识别案例

    import pandas as pd import numpy as np import matplotlib.pyplot as plt import os from sklearn.neighb ...

  4. swagger2文档使用

    ①.导入依赖 <dependency> <groupId>io.springfox</groupId> <artifactId>springfox-sw ...

  5. Using Keyboard Navigation

    http://technet.microsoft.com/en-us/library/cc939835.aspx

  6. 自定义InputFormat

    回顾: 在上一篇https://www.cnblogs.com/superlsj/p/11857691.html详细介绍了InputFormat的原理和常见的实现类.总结来说,一个InputForma ...

  7. 2018-2-13-C#-Find-vs-FirstOrDefault

    title author date CreateTime categories C# Find vs FirstOrDefault lindexi 2018-2-13 17:23:3 +0800 20 ...

  8. resolver - 解析器(resolver) 配置文件

    总览 (SYNOPSIS) /etc/resolv.conf 描述 (DESCRIPTION) 解析器(resolver) 是 C 函数库 中 的 一组 例程, 用于 访问 Internet 域名系统 ...

  9. 330-支持PXIE带FMC接口的Xilinx FPGA XC7K325T PCIeX8 接口卡平台

    支持PXIE带FMC接口的Xilinx FPGA XC7K325T PCIeX8 接口卡平台 一.板卡概述     本板卡基于Xilinx公司的FPGAXC7K325T-2FFG900 芯片,pin_ ...

  10. Oracle 反键索引/反向索引

    反键索引又叫反向索引,不是用来加速数据访问的,而是为了均衡IO,解决热块而设计的比如数据这样: 1000001 1000002 1000005 1000006 在普通索引中会出现在一个叶子上,如果部门 ...