多线程 队列 python_从零开始学Python(八):Python多线程和队列

很久没有更新博文啦,在家过春节已经变懒了-_-,不过答应大家更完这个python的入门系列,偶还是会继续努力的!另外祝愿大家新年快乐,事事顺心!

线程的概念

我们学习的很多编程语言,比如java,oc等,都会有线程这个概念.线程的用途非常的广泛,给我们开发中带来了很多的便利.主要用于一些串行或者并行的逻辑处理,比如点击某个按钮的时候,我们可以通过进度条来控制线程的运行时间,以便于更好的用于用户的交互.

每个独立的线程都包含一个程序的运行入口,顺序的执行序列和一个程序运行的出口.线程必须在程序中存在,而不能独立于程序运行!

每个线程都有他自己的一组cpu储存器,称为线程的上下文,该上下文反应了线程上次运行的cpu寄存器的状态.指令指针和堆栈指针寄存器是线程上下文中两个最重要的寄存器,线程总是在进程得到上下文运行,这些地址都用于标志拥有线程的进程地址空间中的内存.

Python线程

在Python中,主要提供了thread和threading两个线程模块,thread模块提供了最基础的,最低级的线程函数,和一个简单的锁.threading模块是thread模块的封装进阶,提供了多样的线程属性和方法.下面我们会对该两个模块逐个解析.

thread模块(不推荐使用)

thread模块常用的函数方法:

函数名

描述

start_new_thread(function, args, kwargs=None)

产生一个新线程,function为线程要运行的函数名,args是函数参数(tuple元组类型),kwargs为可选参数

allocate_lock()

分配一个locktype类型的线程锁对象

exit()

线程退出

_count()

返回线程数量,注意不包含主线程哦,所以在主线程运行该方法返回的是0

locked

locktype 锁,返回true为已锁

release()

释放locktype对象锁

acquire()

锁定

下面我们来举个例子:

import thread,time

def loop1():

print '线程个数-' + str(thread._count())

i=0

try:

while i < 100:

print i

time.sleep(1)

i = i + 1

except Exception as e:

print e

thread.start_new_thread(loop1,())

复制代码

运行上面代码,你会发现loop1方法中的循环打印并没有被调用,而是直接返回了一个异常:

Unhandled exception in thread started by

sys.excepthook is missing

lost sys.stderr

复制代码

这时你可能会一遍又一遍的检查代码,以为是代码错了(没错,那个人就是我),其实我们代码本身是没有错误的,是早期python的thread模块一个缺陷(这个缺陷也是导致这个模块被官方不推荐使用的主要原因):当我们在主线程中使用start_new_thread创建新的线程的时候,主线程无法得知线程何时结束,他不知道要等待多久,导致主线程已经执行完了,子线程却还未完成,于是系统抛出了这个异常.

解决这个异常的方法有两种:

1.让主线程休眠足够长的时间来等待子线程返回结果:

import thread,time

def loop1():

print '线程个数-' + str(thread._count())

i=0

try:

while i < 100:

print i

time.sleep(1)

i = i + 1

except Exception as e:

print e

thread.start_new_thread(loop1,())

time.sleep(1000) #让主线程休眠1000秒,足够子线程完成

复制代码

2.给线程加锁(早期python线程使用一般处理)

import thread,time

def loop1(lock):

print '线程个数-' + str(thread._count())

i=0

try:

while i < 100:

print i

time.sleep(1)

i = i + 1

except Exception as e:

lock.release()

print e

lock.release() #执行完毕,释放锁

lock=thread.allocate_lock() #获取locktype对象

lock.acquire() #锁定

thread.start_new_thread(loop1,(lock,))

while lock.locked(): #等待线程锁释放

pass

复制代码

以上就是thread模块的常用线程用法,我们可以看出,thread模块提供的线程操作是极其有限的,使用起来非常的不灵活,下面我们介绍他的同胞模块threading.

threading模块(推荐使用)

threading模块是thread的完善,有一套成熟的线程操作方法,基本能完成我们所需的所有线程操作

threading常用方法:

threading.currentThread(): 返回当前的线程变量。

threading.enumerate(): 返回一个包含正在运行的线程的list。 正在运行指线程启动后、结束前,不包括启动前和终止后的线程。

threading.activeCount(): 返回正在运行的线程数量,与len(threading.enumerate())有相同的结果。

run(): 用以表示线程活动的方法。

start():启动线程活动。

join([time]): 等待至线程中止。 这阻塞调用线程直至线程的join() 方法被调用中止-正常退出或者抛出未处理的异常-或者是可选的超时发生。

isAlive(): 返回线程是否活动的。

getName(): 返回线程名。

setName(): 设置线程名。

threading模块创建线程有两种方式:

1.直接通过初始化thread对象创建:

#coding=utf-8

import threading,time

def test():

t = threading.currentThread() # 获取当前子线程对象

print t.getName() # 打印当前子线程名字

i=0

while i<10:

print i

time.sleep(1)

i=i+1

m=threading.Thread(target=test,args=(),name='循环子线程') #初始化一个子线程对象,target是执行的目标函数,args是目标函数的参数,name是子线程的名字

m.start()

t=threading.currentThread() #获取当前线程对象,这里其实是主线程

print t.getName() #打印当前线程名字,其实是主线程名字

复制代码

可以看到打印结果:

循环子线程

MainThread

0

1

2

3

4

5

6

7

8

复制代码

2.通过基础thread类来创建

import threading,time

class myThread (threading.Thread): #创建一个自定义线程类mythread,继承Thread

def __init__(self,name):

"""

重新init方法

:param name: 线程名

"""

super(myThread, self).__init__(name=name)

# self.lock=lock

print '线程名'+name

def run(self):

"""

重新run方法,这里面写我们的逻辑

:return:

"""

i=0

while i<10:

print i

time.sleep(1)

i=i+1

if __name__=='__main__':

t=myThread('mythread')

t.start()

复制代码

输出:

线程名线程

0

1

2

3

4

5

6

7

8

9

复制代码

线程同步

如果两个线程同时访问同一个数据的时候,可能会出现无法预料的结果,这时候我们就要用到线程同步的概念.

上面我们讲到thread模块的时候,已经使用了线程锁的概念,thread对象的Lock和Rlock可以实现简单的线程同步,这两个对象都有acquire方法和release方法,对于那些需要每次只允许一个线程操作的数据,可以将其操作放到acquire和release方法之间.

下面我们来举例说明,我们需要实现3个线程同时访问一个全局变量,并且改变这个变量:

1.不加锁的情况

import threading,time

lock=threading.Lock() #全局的锁对象

temp=0 #我们要多线程访问的全局属性

class myThread (threading.Thread): #创建一个自定义线程类mythread,继承Thread

def __init__(self,name):

"""

重新init方法

:param name: 线程名

"""

super(myThread, self).__init__(name=name)

# self.lock=lock

print '线程名'+name

def run(self):

"""

重新run方法,这里面写我们的逻辑

:return:

"""

global temp,lock

i=0

while i<2: #这里循环两次累加全局变量,目的是增加出错的概率

temp=temp+1 #在子线程中实现对全局变量加1

print self.name+'--temp=='+str(temp)

i=i+1

if __name__=='__main__':

t1=myThread('线程1')

t2=myThread('线程2')

t3=myThread('线程3')

#创建三个线程去执行访问

t1.start()

t2.start()

t3.start()

复制代码

执行结果(由于程序运行很快,你多运行几次就可能会出现以下结果): 我们可以发现,线程1和线程2同时访问到了变量,导致打印出现对等情况

线程名线程1

线程名线程2

线程名线程3

线程1--temp==1线程2--temp==2

线程1--temp==3

线程2--temp==4

线程3--temp==5

线程3--temp==6

复制代码

2.加锁情况

import threading,time

lock=threading.Lock() #全局的锁对象

temp=0 #我们要多线程访问的全局属性

class myThread (threading.Thread): #创建一个自定义线程类mythread,继承Thread

def __init__(self,name):

"""

重新init方法

:param name: 线程名

"""

super(myThread, self).__init__(name=name)

# self.lock=lock

print '线程名'+name

def run(self):

"""

重新run方法,这里面写我们的逻辑

:return:

"""

global temp,lock

if lock.acquire(): #这里线程进来访问变量的时候,锁定变量

i = 0

while i < 2: # 这里循环两次累加全局变量,目的是增加出错的概率

temp = temp + 1 # 在子线程中实现对全局变量加1

print self.name + '--temp==' + str(temp)

i = i + 1

lock.release() #访问完毕,释放锁让另外的线程访问

if __name__=='__main__':

t1=myThread('线程1')

t2=myThread('线程2')

t3=myThread('线程3')

#创建三个线程去执行访问

t1.start()

t2.start()

t3.start()

复制代码

运行结果(不管运行多少次,都不会出现同时访问的情况):

线程名线程1

线程名线程2

线程名线程3

线程1--temp==1

线程1--temp==2

线程2--temp==3

线程2--temp==4

线程3--temp==5

线程3--temp==6

复制代码

线程同步很多地方都会用到,比如抢票,抽奖,我们需要对一些资源进行锁定,以防止多线程访问的时候出现不可预知的情况.

线程队列

python中的队列用到了Queue模块,该模块提供了同步的,安全的对序列,包括FIFO(先入先出)队列Queue,LIFO(后入先出)队列LifoQueue,和优先级队列PriorityQueue.这些队列都实现了锁原语,能够在多线程中直接使用。可以使用队列来实现线程间的通信

Queue模块中的常用方法:

Queue.qsize() 返回队列的大小

Queue.empty() 如果队列为空,返回True,反之False

Queue.full() 如果队列满了,返回True,反之False

Queue.full 与 maxsize 大小对应

Queue.get([block[, timeout]])获取队列,timeout等待时间

Queue.get_nowait() 相当Queue.get(False)

Queue.put(item) 写入队列,timeout等待时间

Queue.put_nowait(item) 相当Queue.put(item, False)

Queue.task_done() 在完成一项工作之后,Queue.task_done()函数向任务已经完成的队列发送一个信号

Queue.join() 实际上意味着等到队列为空,再执行别的操作

例子:

tags=['one','tow','three','four','five','six']

q=Queue.LifoQueue() #先入先出队列

for t in tags:

q.put(t) #将数组数据加入队列

for i in range(6):

print q.get() #取出操作可以放在不同的线程中,不会出现同步的问题

复制代码

结果:

six

five

four

three

tow

one

复制代码

Q&A

这章的多线程就到这里了,我们主要讲述了他的基本用法,更多的用法我们可以在以后的开发过程中,根据自己逻辑去设计.

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值