Python多线程编程

简介

多线程编程适用任务对象为该任务:
1.本质上是异步的;
2.需要多个并发活动;
3.每个活动的处理顺序可能是不确定的,或者说是随机的、不可预测的。

使用多线程编程,以及类似Queue的共享数据结构,这个编程任务可以规划成几个执行特定函数的线程。

UserRequestThread:
 负责读取客户端输入,该输入可能来自于I/O通道。

RequsetProcessor:
该线程负责从队列中获取请求并进行处理。

ReplyThread:
负责向用户输出,将结果传回给用户(如果是网络应用),或者把数据写到本地文件系统或者数据库中。

线程和进程

进程

进程是一个执行中的程序。每个进程都有自己的地址空间、内存、数据栈以及其他用于跟踪执行的辅助数据。进程可以通过派生(fork或spawn)新的进程来执行其他任务,不过因为每个新进程也都拥有自己的内存和数据栈等,所以只能采用进程间通信(IPC)的方式共享信息。

线程

线程包括开始、执行程序、结束三个部分。它有一个临时指针,用于记录当前运行的上下文。

让步:当其他线程运行时,它可以被抢占(中断)和临时挂起(睡眠)

在Python中使用线程

使用单线程执行循环

首先,创建两个时间循环:一个睡眠4秒,一个睡眠2秒。如果在一个单线程或者单线程的程序中执行,就会至少达到6秒。在启动和执行其他代码时,也可能存在一秒的开销,使得进程时间达到七秒。
在这里插入图片描述
执行结果如下:
在这里插入图片描述

Python的threading模块

Python提供了多个模块来支持多线程编程,包括thread、threading和Queue模块等。程序是可以使用thread、threading模块来创建与管理线程。thread模块提供了基本的线程和锁定支持;而threading模块提供了更高级别、功能更全面的线程管理。使用Queue模块,用户可以创建一个队列数据结构,用于在多线程之间进行共享。

thread模块

除了派生之外,thread模块还提供了基本的同步数据结构,成为锁对象。
thread模块的核心函数是start_new_thread()。它的参数包括函数(对象)、函数的参数以及可选的关键字参数。将专门派生新的线程来调用这个函数。

thread模块和锁对象:

一、thread模块的函数:
1.派生一个新的线程
start_new_thread(function,args,kwargs=None)

2.分配LockType锁对象
allocate_lock()

3.给线程退出指令
exit()

二、LockType锁对象的方法
1.尝试获取锁对象
acquire(wait=None)

2.如果获取了锁对象返回True,否则,返回False
locked()

3.释放锁
release()

下面是一个实例:
mtsleepA.py
在这里插入图片描述
start_new_thread()必须包含开始的两个参数,于是即使要执行的函数不需要参数,也需要传递一个空元组。

执行结果如下:
在这里插入图片描述
与之前的onethr.py相比,原本需要运行6-7秒,本程序脚本运行时间只需要4秒,也就是最长的循环加上其他所有开销的时间之和。

然而我们没有写让主线程等待子线程全部完成后再继续的代码,即我们所说的线程需要某种形式的同步。在这个实例中,调用sleep(6)是因为我们知道所有线程会在主线程计时到6秒前完成。

所以,为了改进代码,可以引入锁,并去除单独的循环函数。修改实例如下:
在这里插入图片描述
执行结果如下:
在这里插入图片描述
然而,在这里使用thread模块只是为了向大家介绍多线程编程,多线程应用程序应当使用更高级别的模块,比如接下来要介绍的threading模块。

threading模块

除了Thread类以外,threading模块还包括了许多非常好用的同步机制。下面就来介绍一下:

Thread: 表示执行一个线程的对象
Lock: 锁原语对象
RLock: 可重入锁对象,使单一线程可以(再次)获得已有的锁(递归锁)
Condition: 条件变量对象,使得一个线程等待另外一个线程满足特定条件,比如改变状态成某个数据值
Event: 条件变量的通用版本,任意数量的线程等待某个事件的发生,在该事件发生后所有线程将会被激活
Semaphore: 为线程间共享的有限资源提供一个“计数器”,如果没有可用资源时会被阻塞
BoundedSemaphore: 与Semaphore相似,但不允许超过初始值
Timer: 与Thread相似,不过它需要在运行前等待一段时间
Barrier: 创建一个“障碍”,必须达到指定数量的线程后才可以继续

避免使用thread模块的另外一个原因就是该模块不支持守护线程这个概念。什么是守护线程?
当主线程退出时,所有子线程都将终止,不管它们是否在工作。
threading模块支持守护线程,其工作方式是:守护线程一般是一个等待客户端请求服务的服务器。如果没有客户端请求,守护线程就是空闲的。如果把一个线程设置为守护线程,就表示这个线程是不重要的,进程退出时不需要等待这个线程执行完成。
如果主线程准备退出时,不需要等待某些子线程完成,就可以为这些子线程设置守护线程标记。

要将一个线程设置为守护线程,需要在启动线程之前执行下语句:
_thread.daemon = True
同样,要检查线程是否为守护状态,就要检查这个值是否为True

Thread类

Thread对象数据属性:

name : 线程名
ident : 线程的标识符
daemon : 布尔标志,表示这个线程是否是守护线程

Thread对象方法:

实例化一个线程对象:_init_(group=None, tatget=None, name=None, 
args=(), kwargs={},verbose=None, daemon=None)

 start():开始执行该线程
run():定义线程功能的方法 
join(timeout=None):直到启动的线程终止之前一直挂起,除非给出timeout,否则一直阻塞

使用Thread类创建线程的方法:

1、创建Thread实例,传给它一个参数
2、创建Thread实例,传给它一个可调用的类实例
3、创建Thread实例,并创建子类的实例

方法一:创建Thread的实例,传给它一个参数

在这里插入图片描述
执行结果如下:
在这里插入图片描述

方法二:派生Thread的子类,并创建子类的实例

mtsleepE.py
在这里插入图片描述
在这里插入图片描述

执行结果如下:

在这里插入图片描述

Python多线程实践:

图书馆排名示例

下面先写一个没有使用线程版本的实例:
bookrank.py

from atexit import register
from re import compile
from threading import Thread
from time import ctime
from urllib.request import urlopen as uopen

REGEX = compile(' #([\d,]+) in Books ')
AMZN = 'http://amazon.com/dp/'
ISBNS = {
    '0132269937': 'Core Python Programming',
    '0132356139': 'Python Web Development with Django',
    '0139143419': 'Python Fundamentals',
}

def getRanking(isbn):
    page = uopen('%s%s' % (AMZN, isbn))
    data =page.close()
    page.close()
    return REGEX.findall(data)[0]

def _showRanking(isbn):
    print('- %r ranked %s' % (
        ISBNS[isbn], getRanking(isbn)))

def main():
    print('At', ctime(), 'on Amazon...')
    for isbn in ISBNS:
        _showRanking(isbn)

@register
def _atexit():
    print('all done at:', ctime())

if __name__ == "__main__":
    main()

执行结果如下:
在这里插入图片描述
可见只用单线程的话程序运行十分缓慢。

由于这是一个I/O密集型应用,因此这个程序使用多线程是一个好的选择。简单起见,直接将应用中的_showRanking(isbn)进行修改即可:

def _showRanking(isbn):
    Thread(target=_showRanking, args=(isbn,)).start()
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值