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中使用装饰器时需要注意的一些问题
May 11 Python
python数据处理实战(必看篇)
Jun 11 Python
python实现txt文件格式转换为arff格式
May 31 Python
Windows 64位下python3安装nltk模块
Sep 19 Python
python实现列表中最大最小值输出的示例
Jul 09 Python
这可能是最好玩的python GUI入门实例(推荐)
Jul 19 Python
django 数据库连接模块解析及简单长连接改造方法
Aug 29 Python
django之导入并执行自定义的函数模块图解
Apr 01 Python
Python devel安装失败问题解决方案
Jun 09 Python
浅谈keras 模型用于预测时的注意事项
Jun 27 Python
Python定时任务框架APScheduler原理及常用代码
Oct 05 Python
pytorch finetuning 自己的图片进行训练操作
Jun 05 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 日常开发小技巧
2009/09/23 PHP
PHP内核介绍及扩展开发指南―基础知识
2011/09/11 PHP
PHP开启opcache提升代码性能
2015/04/26 PHP
详解WordPress中过滤链接与过滤SQL语句的方法
2015/12/18 PHP
PHP生成腾讯云COS接口需要的请求签名
2018/05/20 PHP
laravel实现图片上传预览,及编辑时可更换图片,并实时变化的例子
2019/11/14 PHP
Javascript实例教程(19) 使用HoTMetal(3)
2006/12/23 Javascript
JavaScript实现动态增加文件域表单
2009/02/12 Javascript
Jquery 获取表单text,areatext,radio,checkbox,select值的代码
2009/11/12 Javascript
javascript 节点排序 2
2011/01/31 Javascript
jQuery侧边栏实现代码
2016/05/06 Javascript
折叠菜单及选择器的运用
2017/02/03 Javascript
详解Vue用自定义指令完成一个下拉菜单(select组件)
2017/10/31 Javascript
webpack构建换肤功能的思路详解
2017/11/27 Javascript
Vue 2.0学习笔记之使用$refs访问Vue中的DOM
2017/12/19 Javascript
JavaScript实现快速排序的方法分析
2018/01/10 Javascript
微信jssdk逻辑在vue中的运用详解
2018/11/14 Javascript
OpenLayer学习之自定义测量控件
2020/09/28 Javascript
python基础教程之数字处理(math)模块详解
2014/03/25 Python
Python采用socket模拟TCP通讯的实现方法
2014/11/19 Python
Python实现将MySQL数据库表中的数据导出生成csv格式文件的方法
2018/01/11 Python
Python实现爬虫设置代理IP和伪装成浏览器的方法分享
2018/05/07 Python
Django使用paginator插件实现翻页功能的实例
2018/10/24 Python
Python使用enumerate获取迭代元素下标
2020/02/03 Python
python 成功引入包但无法正常调用的解决
2020/03/09 Python
如何写python的配置文件
2020/06/07 Python
Python偏函数Partial function使用方法实例详解
2020/06/17 Python
详细分析Python collections工具库
2020/07/16 Python
CSS3模块的目前的状况分析
2010/02/24 HTML / CSS
HTML5中的nav标签学习笔记
2016/06/24 HTML / CSS
HTML5 placeholder属性详解
2016/06/22 HTML / CSS
七年级音乐教学反思
2014/01/26 职场文书
2014年教师节红领巾广播稿
2014/09/10 职场文书
美德少年主要事迹材料
2015/11/04 职场文书
游戏《东方异文石:爱亚利亚黎明》正式版发布
2022/04/03 其他游戏
MySQL数据库实验实现简单数据库应用系统设计
2022/06/21 MySQL