python多线程使用方法实例详解


Posted in Python onDecember 30, 2019

本文实例讲述了python多线程使用方法。分享给大家供大家参考,具体如下:

threading 模块支持守护线程, 其工作方式是:守护线程一般是一个等待客户端请求服务的服务器。

如果把一个线程设置为守护线程,进程退出时不需要等待这个线程执行完成。

如果主线程准备退出时,不需要等待某些子线程完成,就可以为这些子线程设置守护线程标记。 需要在启动线程之前执行如下赋值语句: thread.daemon = True,检查线程的守护状态也只需要检查这个值即可。

整个 Python 程序将在所有非守护线程退出之后才退出, 换句话说, 就是没有剩下存活的非守护线程时才退出。

使用thread模块

以下是三种使用 Thread 类的方法(一般使用第一个或第三个方案)

  • 创建 Thread 的实例,传给它一个函数。
import threading
from time import sleep, ctime
loops = [3, 2, 1, 1, 1]
def loop(i, nsec):
  print(f'start loop {i} at: {ctime()}')
  sleep(nsec)
  print(f'end loop {i} at: {ctime()}')
def main():
  print('start at', ctime())
  threads = []
  nloops = range(len(loops))
  for i in nloops:
    t = threading.Thread(target=loop, args=(i, loops[i]))
    threads.append(t)
  for i in nloops: # start threads
    threads[i].start()
  for i in nloops: # wait for all
    threads[i].join() # threads to finish
  print(f'all done at: {ctime()}')
if __name__ == '__main__':
  main()

当所有线程都分配完成之后,通过调用每个线程的 start()方法让它们开始执行,而不是 在这之前就会执行。
相比于管理一组锁(分配、获取、释放、检查锁状态等)而言,这里只 需要为每个线程调用 join()方法即可。
join()方法将等待线程结束,或者在提供了超时时间的情况下,达到超时时间。
使用 join()方法要比等待锁释放的无限循环更加清晰(这也是这种锁 又称为自旋锁的原因)。

  • 创建 Thread 的实例,传给它一个可调用的类实例。
import threading
from time import sleep, ctime
# 创建 Thread 的实例,传给它一个可调用的类实例
loops = [3, 2, 1, 1, 1]
class ThreadFunc(object):
  def __init__(self, func, args, name=''):
    self.name = name
    self.func = func
    self.args = args
  def __call__(self):
    self.func(*self.args)
def loop(i, nsec):
  print(f'start loop {i} at: {ctime()}')
  sleep(nsec)
  print(f'end loop {i} at: {ctime()}')
def main():
  print('start at', ctime())
  threads = []
  nloops = range(len(loops))
  for i in nloops:
    t = threading.Thread(target=ThreadFunc(loop, (i, loops[i]), loop.__name__))
    threads.append(t)
  for i in nloops: # start threads
    threads[i].start()
  for i in nloops: # wait for all
    threads[i].join() # threads to finish
  print(f'all done at: {ctime()}')
if __name__ == '__main__':
  main()
  • 派生 Thread 的子类,并创建子类的实例。
import threading
from time import sleep, ctime
# 创建 Thread 的实例,传给它一个可调用的类实例
# 子类的构造函数必须先调用其基类的构造函数
# 特殊方法__call__()在 子类中必须要写为 run()
loops = [3, 2, 1, 1, 1]
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 loop(i, nsec):
  print(f'start loop {i} at: {ctime()}')
  sleep(nsec)
  print(f'end loop {i} at: {ctime()}')
def main():
  print('start at', ctime())
  threads = []
  nloops = range(len(loops))
  for i in nloops:
    t = MyThread(loop, (i, loops[i]), loop.__name__)
    threads.append(t)
  for i in nloops: # start threads
    threads[i].start()
  for i in nloops: # wait for all
    threads[i].join() # threads to finish
  print(f'all done at: {ctime()}')
if __name__ == '__main__':
  main()

使用锁

python和java一样,也具有锁机制,而且创建与使用锁都是很简便的。

一般在多线程代码中,总会有一些特 定的函数或代码块不希望(或不应该)被多个线程同时执行,通常包括修改数据库、更新文件或 其他会产生竞态条件的类似情况

锁有两种状态:锁定和未锁定。而且它也只支持两个函数:获得锁和释放锁。

一般锁的调用如下

# 加载线程的锁对象
lock = threading.Lock()
# 获取锁
lock.acquire()
# ...代码
# 释放锁
lock.release()

更简洁的方法是使用with关键字,如下代码功能同上

# 加载线程的锁对象
lock = threading.Lock()
with lock :
  #...代码

示例代码:

import threading
from time import sleep, ctime
lock = threading.Lock()
def a():
  lock.acquire()
  for x in range(5):
    print(f'a:{str(x)}')
    sleep(0.01)
  lock.release()
def b():
  lock.acquire()
  for x in range(5):
    print(f'a:{str(x)}')
    sleep(0.01)
  lock.release()
threading.Thread(target=a).start()
threading.Thread(target=b).start()

相关属性和方法

  • Thread对象的属性

属性 描述
name 线程名
ident 线程的标识符
daemon 布尔标志,表示这个线程是否是守护线程
 
  • Thread对象的方法

方法 描述
init(group=None, tatget=None, name=None, args=(), kwargs ={}, verbose=None, daemon=None) 实例化一个线程对象,需要有一个可调用的 target,以及其参数 args 或 kwargs。还可以传递 name 或 group 参数,不过后者还未实现。此 外 , verbose 标 志 也 是 可 接 受 的 。 而 daemon 的 值 将 会 设 定 thread.daemon 属性/标志
start() 开始执行该线程
run() 定义线程功能的方法(通常在子类中被应用开发者重写)
join (timeout=None) 直至启动的线程终止之前一直挂起;除非给出了 timeout(秒),否则 会一直阻塞
is_alive() 布尔标志,表示这个线程是否还存活
  • threading模块其他函数

函数 描述
start() 开始执行该线程
active_count() 当前活动的 Thread 对象个数
enumerate() 返回当前活动的 Thread 对象列表
settrace(func) 为所有线程设置一个 trace 函数
setprofile (func) 为所有线程设置一个 profile 函数
stack_size(size=0) 返回新创建线程的栈大小;或为后续创建的线程设定栈的大小 为 size
Lock() 加载线程的锁对象,是一个基本的锁对象,一次只能一个锁定,其余锁请求,需等待锁释放后才能获取,对象有acquire()和release()方法
RLock() 多重锁,在同一线程中可用被多次acquire。如果使用RLock,那么acquire和release必须成对出现,调用了n次acquire锁请求,则必须调用n次的release才能在线程中释放锁对象

后记

在Python多线程下,每个线程的执行方式:

1、获取GIL
2、执行代码直到sleep或者是python虚拟机将其挂起。
3、释放GIL

通常来说,多线程是一个好东西。不过由于 Python 的 GIL 的限制,多线程更适合于 I/O 密集型应用(I/O 释放了 GIL,可以允 许更多的并发),而不是计算密集型应用。对于后一种情况而言,为了实现更好的并行性,你需要使用多进程,以便让 CPU 的其他内核来执行。

请注意:多核多线程比单核多线程更差,原因是单核下多线程,每次释放GIL,唤醒的那个线程都能获取到GIL锁,所以能够无缝执行,但多核下,CPU0释放GIL后,其他CPU上的线程都会进行竞争,但GIL可能会马上又被CPU0拿到,导致其他几个CPU上被唤醒后的线程会醒着等待到切换时间后又进入待调度状态,这样会造成线程颠簸(thrashing),导致效率更低

希望本文所述对大家Python程序设计有所帮助。

Python 相关文章推荐
python删除文件示例分享
Jan 28 Python
python获取Linux下文件版本信息、公司名和产品名的方法
Oct 05 Python
python实现的用于搜索文件并进行内容替换的类实例
Jun 28 Python
Python连接MySQL并使用fetchall()方法过滤特殊字符
Mar 13 Python
Python实现两个list对应元素相减操作示例
Jun 09 Python
Python 加密的实例详解
Oct 09 Python
浅析Python四种数据类型
Sep 26 Python
python3 线性回归验证方法
Jul 09 Python
详解用python生成随机数的几种方法
Aug 04 Python
Python学习笔记之For循环用法详解
Aug 14 Python
python代码中怎么换行
Jun 17 Python
python基础学习之生成器与文件系统知识总结
May 25 Python
Python动态声明变量赋值代码实例
Dec 30 #Python
使用pytorch实现可视化中间层的结果
Dec 30 #Python
在Pytorch中计算自己模型的FLOPs方式
Dec 30 #Python
Pytorch之保存读取模型实例
Dec 30 #Python
Python爬虫解析网页的4种方式实例及原理解析
Dec 30 #Python
Python中如何将一个类方法变为多个方法
Dec 30 #Python
pytorch 实现打印模型的参数值
Dec 30 #Python
You might like
PHP fgetcsv 定义和用法(附windows与linux下兼容问题)
2012/05/29 PHP
PHP 数组基本操作方法详解
2016/06/17 PHP
PHP中include和require的区别实例分析
2017/05/07 PHP
解决Laravel5.5下的toArray问题
2019/10/15 PHP
javascript编程起步(第一课)
2007/01/10 Javascript
js一组验证函数
2008/12/20 Javascript
javascript一个无懈可击的实例化XMLHttpRequest的方法
2010/10/13 Javascript
script的async属性以非阻塞的模式加载脚本
2013/01/15 Javascript
利用javaScript实现点击输入框弹出窗体选择信息
2013/12/11 Javascript
JavaScript图片轮播代码分享
2015/07/31 Javascript
jQuery中实现prop()函数控制多选框(全选,反选)
2016/08/19 Javascript
jQuery.parseHTML() 函数详解
2017/01/09 Javascript
深入理解vue.js中$watch的oldvalue与newValue
2017/08/07 Javascript
jQuery实现新闻播报滚动及淡入淡出效果示例
2018/03/23 jQuery
原生JS实现的简单小钟表功能示例
2018/08/30 Javascript
vue实现添加与删除图书功能
2018/10/07 Javascript
vue项目实现图片上传功能
2019/12/23 Javascript
详解vue-router的导航钩子(导航守卫)
2020/11/02 Javascript
[01:00:44]DOTA2上海特级锦标赛主赛事日 - 3 败者组第三轮#1COL VS Alliance第三局
2016/03/04 DOTA
[50:05]VGJ.S vs OG 2018国际邀请赛淘汰赛BO3 第二场 8.22
2018/08/23 DOTA
跟老齐学Python之list和str比较
2014/09/20 Python
Python 两个列表的差集、并集和交集实现代码
2016/09/21 Python
pandas数据预处理之dataframe的groupby操作方法
2018/04/13 Python
对Python中数组的几种使用方法总结
2018/06/28 Python
Python遍历文件夹 处理json文件的方法
2019/01/22 Python
python 机器学习之支持向量机非线性回归SVR模型
2019/06/26 Python
python图形开发GUI库pyqt5的基本使用方法详解
2020/02/14 Python
Django操作session 的方法
2020/03/09 Python
python基本算法之实现归并排序(Merge sort)
2020/09/01 Python
Saks Fifth Avenue澳洲/亚太地区:萨克斯第五大道精品百货店
2019/06/09 全球购物
Shop Apotheke瑞士:您的健康与美容网上商店
2019/10/09 全球购物
公司端午节活动方案
2014/02/04 职场文书
学生会副主席竞聘书
2014/03/31 职场文书
2014年医生工作总结
2014/11/21 职场文书
看上去很美观后感
2015/06/10 职场文书
2016年学生会感恩节活动总结
2016/04/01 职场文书