Python3 多线程实例学习

什么是线程(thread)

线程(有时被称为轻量级进程)跟进程有些相似,不同的是,所有的线程运行在同一个进程中,共享相同的运行环境。

线程的运行可能被抢占(中断),或暂时的被挂起(也叫睡眠),让其他的线程运行, 这叫做让步。

Python3 线程中常用的两个模块为:

  • _thread
  • threading(推荐使用)

全局解释器锁(GIL)

Global Interpreter Lock(GIL),任何Python线程执行前,必须先获得GIL锁,然后,每执行100条字节码,解释器就自动释放GIL锁,让别的线程有机会执行。这个GIL全局锁实际上把所有线程的执行代码都给上了锁,所以,多线程在Python中只能交替执行,即使100个线程跑在100核CPU上,也只能用到1个核。

thraeding

方法

threading方法:

  • threading.currentThread(): 返回当前的线程变量。
  • threading.enumerate(): 返回一个包含正在运行的线程的list。正在运行指线程启动后、结束前,不包括启动前和终止后的线程。
  • threading.activeCount(): 返回正在运行的线程数量,与len(threading.enumerate())有相同的结果。

Thread类方法:

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

应用

1.创建一个Thread的实例,传给它一个函数;

#!/usr/bin/env python3
# -*- conding:utf-8 -*-

import threading
from time import sleep,ctime

def loop(nloop,nsec):
    print('start loop',nloop,threading.current_thread().name,'at',ctime())
    sleep(nsec)
    print('stop loop',nloop,'at',ctime())

def main():
    loops = [2,4]
    length = len(loops)
    threads = []
    print('program is start at',ctime())

    for i in range(length):
        t = threading.Thread(target=loop,args=(i,loops[i]))
        threads.append(t)

    for i in range(length):
        threads[i].start()

    for i in range(length):
        threads[i].join()

    print('porgram is stop at',ctime())

if __name__ == '__main__':
    main()

运行结果

program is start at Tue Apr 23 12:00:08 2019
start loop 0 Thread-1 at Tue Apr 23 12:00:08 2019
start loop 1 Thread-2 at Tue Apr 23 12:00:08 2019
stop loop 0 at Tue Apr 23 12:00:10 2019
stop loop 1 at Tue Apr 23 12:00:12 2019
porgram is stop at Tue Apr 23 12:00:12 2019

所有的线程都创建了之后,再一起调用start()函数启动,而不是创建一个启动一个。而且,不用再管理一堆锁(分配锁、获得锁、释放锁、检查锁的状态等),只要简单地对每个线程调用join()函数就可以了。

join()会等到线程结束,或者在给了timeout参数的时候,等到超时为止。使用join()看上去会比使用一个等待锁释放的无限循环清楚一些(这种锁也被称为“自旋锁”)。

join()的另一个比较重要的方面是它可以完全不用调用。一旦线程启动后,就会一直运行,直到线程的函数结束,退出为止。如果你的主线程除了等线程结束外,还有其他的事情要做(如处理或等待其他的客户请求),那就不用调用join(),只有在你要等待线程结束的时候才要调用join()。

2.从Thread派生出一个子类,创建一个这个子类的实例。

#!/usr/bin/env python3
# -*- conding:utf-8 -*-

import threading
from time import sleep,ctime

class MyThread(threading.Thread): #创建子类
    def __init__(self,func,args,name=''):
        threading.Thread.__init__(self)
        self.name = name
        self.func = func
        self.args = args

    def run(self): #改写run方法
       self.func(*self.args)


def loop(nloop,nsec):
    print('start loop',nloop,threading.current_thread().name,'at',ctime())
    sleep(nsec)
    print('stop loop',nloop,'at',ctime())

def main():
    loops = [2,4]
    length = len(loops)
    threads = []
    print('program is start at',ctime())

    for i in range(length):
        t = MyThread(loop,(i,loops[i]))
        threads.append(t)

    for i in range(length):
        threads[i].start()

    for i in range(length):
        threads[i].join()

    print('porgram is stop at',ctime())

if __name__ == '__main__':
    main()

线程同步(Lock)

如果多个线程共同对某个数据修改,则可能出现不可预料的结果,为了保证数据的正确性,需要对多个线程进行同步。

使用 Thread 对象的 Lock 和 Rlock 可以实现简单的线程同步,这两个对象都有 acquire 方法和 release 方法,对于那些需要每次只允许一个线程操作的数据,可以将其操作放到 acquire 和 release 方法之间。如下:

#!/usr/bin/env python3
# -*- conding:utf-8 -*-

import threading
from time import sleep,ctime

lock = threading.Lock() #建立Lock

class MyThread(threading.Thread):
    def __init__(self,func,args,name=''):
        threading.Thread.__init__(self)
        self.name = name
        self.func = func
        self.args = args

    def run(self):
        lock.acquire() #获取锁
        self.func(*self.args)
        lock.release() #释放锁

def loop(nloop,nsec):
    print('start loop',nloop,threading.current_thread().name,'at',ctime())
    sleep(nsec)
    print('stop loop',nloop,'at',ctime())


def main():

    loops = [2,4]
    length = len(loops)
    threads = []
    print('program is start at',ctime())

    for i in range(length):
        t = MyThread(loop,(i,loops[i]))
        threads.append(t)

    for i in range(length):
        threads[i].start()

    for i in range(length):
        threads[i].join()

    print('porgram is stop at',ctime())

if __name__ == '__main__':
    main()

线程优先级队列(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() 实际上意味着等到队列为空,再执行别的操作

演示了生产者和消费者的场景。生产者生产货物,然后把货物放到一个队列之类的数据结构中,生产货物所要花费的时间无法预先确定。消费者消耗生产者生产的货物的时间也是不确定的。

#!/usr/bin/env python3
# -*- conding:utf-8 -*-

import threading
from time import sleep,ctime
from random import randint
from queue import Queue

lock = threading.Lock()

class MyThread(threading.Thread):
    def __init__(self,func,args,name=''):
        threading.Thread.__init__(self)
        self.name = name
        self.func = func
        self.args = args

    def run(self):
        self.func(*self.args)
        
#生产者
def write(queue,nloop): 
    for i in range(nloop):
        queue.put('xx')
        sleep(randint(1,3))
        print('write is running at ',queue.qsize())
        
#消费者
def read(queue,nloop): 
    for i in range(nloop):
        value = queue.get()
        sleep(randint(2,5))
        print('read is running at ',queue.qsize())

def main():

    loops = randint(2,5)
    queue = Queue()
    threads = []
    func = [write,read]
    length = len(func)
    print('program is start at',ctime())

    for i in range(length):
        t = MyThread(func[i],(queue,loops))
        threads.append(t)

    for i in range(length):
        threads[i].start()

    for i in range(length):
        threads[i].join()

    print('porgram is stop at',ctime())

if __name__ == '__main__':
    main()

执行结果

program is start at Tue Apr 23 18:15:53 2019
write is running at  0
write is running at  1
read is running at  1
write is running at  1
write is running at  2
read is running at  3
write is running at  2
read is running at  2
read is running at  1
read is running at  0
porgram is stop at Tue Apr 23 18:16:11 2019

参考

Python3 多线程 | 菜鸟教程
多线程 - 廖雪峰Python教程
《Python核心编程》

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值