Python多线程(一)

线程

​ 一个进程中的各个线程与主线程共享同一片数据空间,因此相对于进程,线程间的信息共享与通讯更加便捷。线程以并发方式执行,得益于这种并行与数据共享的机制,使得多任务协作的实现更加简单。

Python线程模型

​ Python代码的执行是由Python虚拟机控制。在 CPython 中,由于存在 全局解释器锁(GIL),同一时刻只有一个线程可以执行。这种限制使得python的多线程这像在单CPU上跑多进程,只能做到并发,无法做到并行。 。

Python虚拟机按照下面所述方式来切换线程

切换线程被放在一个互斥锁中

  1. 设置GIL

  2. 执行某个线程A

  3. 执行下面操作之一

    1. 执行一定数量的A的python代码(字节码指令)
    2. 线程主动让出控制权
  4. A的执行状态保存,以备下次执行

  5. 解锁GIL

​ 某些 Python I/O 例程(调用了内置的操作系统 C 代码的那种),GIL 会在 I/O 调用前被释放,以允许其他线程在 I/O 执行的时候运行。而对于那些没有太多 I/O 操作的代码而言,更倾向于在该线程整个时间片内始终占有处理器(和 GIL)。总而言之,I/O 密集型的 Python 程序要比计算密集型的代码能够更好地利用多线程环境。

​ 如果想利用多核心计算机的计算资源,推荐使用 multiprocessingconcurrent.futures.ProcessPoolExecutor

Threading模块

未引进线程

def func(name, t):
print(name, "开始", int(time()))
sleep(t)
print(name, "结束", int(time())) print("程序开始执行")
start = time()
func("猪", 4)
func("牛", 4)
end = time()
print("总共运行:",int( end - start))

结果

程序开始执行
猪 开始 1618821733
猪 结束 1618821737
牛 开始 1618821737
牛 结束 1618821741
总共运行: 8

引入线程

方式一:创建Thread实例,传给它一个函数

  • 通过给Thread类构造函数的关键字参数func来定义这个线程将要运行哪个函数,通过args参数来传递func的参数。注意args是一个元组,如果func只需要一个参数时应该这样传参args=(arg1,)。关键字only参数通过kwargs来传入。kwargs是一个字典。
from threading import Thread
from time import sleep, time def func(name, t):
print(name, "开始", int(time()))
sleep(t)
print(name, "结束", int(time())) T1 = Thread(target=func,args=('猪',4))
T2 = Thread(target=func,args=('牛',4)) print("主线程开始执行")
start = time()
T1.start() #线程开始执行
T2.start() #线程开始执行
end = time()
print("主线程共运行:",int( end - start))

结果

主线程开始执行
猪 开始 1618822143
牛 开始 1618822143
主线程共运行: 0
猪 结束 1618822147
牛 结束 1618822147

解释

当调用thread实例T的start()方法时,这时会创建一个独立的线程,在这个线程中运行实例T的run()方法.run()方法默认argskwargs传入target.具体来说就是将 ( '猪' , 4) 传入 func.

当T1.start() T2.start() 调用时,会分别创建两个独立的线程,加上主线程一共有三个独立线程在python虚拟机中运行。三个独立线程以不可预测的进度运行。因此有了上面的运行结果。

注意到,当主线程结束后,整个程序并没有结束。python解释器等到所有的非守护线程结束之后才结束整个程序。这是threading类带来的福利。这也是我们使用threading模块,而不使用更底层的thread模块(这里说的不是Thread类。)的一个原因。

方式二:继承Thread,并重新定义run方法

刚才解释过,调用实例T的start()方法时,会在一个独立的线程中运行实例的run() 方法。run的默认行为是,用构造函数中的target和kwargs来运行target。因此我们可以在子类中重新定义了run方法,在run方法中写出我们想要并发执行的功能。

如果子类型重载了构造函数,它一定要确保在做任何事前,先发起调用基类构造器(Thread.init())。!!!!!

from threading import Thread
from time import sleep, time def func(name, t):
print(name, "开始", int(time()))
sleep(t)
print(name, "结束", int(time())) class MyThread(Thread):
def __init__(self,target,args=(), kwargs={}):
Thread.__init__(self)
#如果子类型重载了构造函数,它一定要确保在做任何事前,先发起调用基类构造器(Thread.__init__())。
self.target = target
self.args = args
self.kwargs = kwargs def run(self):
self.target(*self.args,**(self.kwargs)) print("程序开始执行")
start = time() T1 = MyThread(func,('猪',4))
T2 = MyThread(func,('牛',4))
T1.start()
T2.start() end = time()
print("总共运行:",int( end - start))

结果

程序开始执行
猪 开始 1618824594
牛 开始 1618824594
总共运行: 0
猪 结束 1618824598
牛 结束 1618824598

全局解释器锁GIL

CPython 解释器所采用的一种机制,它确保同一时刻只有一个线程在执行 Python bytecode。此机制通过设置对象模型(包括 dict 等重要内置类型)针对并发访问的隐式安全简化了 CPython 实现。给整个解释器加锁使得解释器多线程运行更方便,其代价则是牺牲了在多处理器上的并行性。

不过,某些标准库或第三方库的扩展模块被设计为在执行计算密集型任务如压缩或哈希时释放 GIL。此外,在执行 I/O 操作时也总是会释放 GIL。

创建一个(以更精细粒度来锁定共享数据的)“自由线程”解释器的努力从未获得成功,因为这会牺牲在普通单处理器情况下的性能。据信克服这种性能问题的措施将导致实现变得更复杂,从而更难以维护

Python 多线程(一)的更多相关文章

  1. python多线程学习记录

    1.多线程的创建 import threading t = t.theading.Thread(target, args--) t.SetDeamon(True)//设置为守护进程 t.start() ...

  2. python多线程编程

    Python多线程编程中常用方法: 1.join()方法:如果一个线程或者在函数执行的过程中调用另一个线程,并且希望待其完成操作后才能执行,那么在调用线程的时就可以使用被调线程的join方法join( ...

  3. Python 多线程教程:并发与并行

    转载于: https://my.oschina.net/leejun2005/blog/398826 在批评Python的讨论中,常常说起Python多线程是多么的难用.还有人对 global int ...

  4. python多线程

    python多线程有两种用法,一种是在函数中使用,一种是放在类中使用 1.在函数中使用 定义空的线程列表 threads=[] 创建线程 t=threading.Thread(target=函数名,a ...

  5. python 多线程就这么简单(转)

    多线程和多进程是什么自行google补脑 对于python 多线程的理解,我花了很长时间,搜索的大部份文章都不够通俗易懂.所以,这里力图用简单的例子,让你对多线程有个初步的认识. 单线程 在好些年前的 ...

  6. python 多线程就这么简单(续)

    之前讲了多线程的一篇博客,感觉讲的意犹未尽,其实,多线程非常有意思.因为我们在使用电脑的过程中无时无刻都在多进程和多线程.我们可以接着之前的例子继续讲.请先看我的上一篇博客. python 多线程就这 ...

  7. python多线程监控指定目录

    import win32file import tempfile import threading import win32con import os dirs=["C:\\WINDOWS\ ...

  8. python多线程ssh爆破

    python多线程ssh爆破 Python 0x01.About 爆弱口令时候写的一个python小脚本,主要功能是实现使用字典多线程爆破ssh,支持ip表导入,字典数据导入. 主要使用到的是pyth ...

  9. 【python,threading】python多线程

    使用多线程的方式 1.  函数式:使用threading模块threading.Thread(e.g target name parameters) import time,threading def ...

  10. <转>Python 多线程的单cpu与cpu上的多线程的区别

    你对Python 多线程有所了解的话.那么你对python 多线程在单cpu意义上的多线程与多cpu上的多线程有着本质的区别,如果你对Python 多线程的相关知识想有更多的了解,你就可以浏览我们的文 ...

随机推荐

  1. 鸿蒙开源第三方组件 ——B站开源弹幕库引擎的迁移(上)

    鸿蒙入门指南,小白速来!0基础学习路线分享,高效学习方法,重点答疑解惑--->[课程入口] 目录: 一.弹幕库的基础知识 二.弹幕库的使用方法 三.sample解析 四.作者系列文章合集 前言 ...

  2. ThreadPoolExecutor中execute和submit的区别

    1:入参不同 excute() 传入的是 Runable, submit 传入的是 Callable 或 Runable 1):execute 方法源码 public void execute(Run ...

  3. 第40天学习打卡(静态代理 Lambda表达式 线程状态 线程同步 同步方法)

    静态代理  package com.kuang.demo03; //静态代理模式总结 //真实对象和代理对象都要实现同一个接口 //代理对象要代理真实角色 //好处:  //代理对象可以做很多真实对象 ...

  4. 后端程序员之路 14、NumPy

    NumPy - NumPyhttp://www.numpy.org/ NumPy-快速处理数据 - 用Python做科学计算http://old.sebug.net/paper/books/scipy ...

  5. 剑指 Offer 51. 数组中的逆序对 + 归并排序 + 树状数组

    剑指 Offer 51. 数组中的逆序对 Offer_51 题目描述 方法一:暴力法(双层循环,超时) package com.walegarrett.offer; /** * @Author Wal ...

  6. CCF(地铁修建):向前星+dijikstra+求a到b所有路径中最长边中的最小值

    地铁修建 201703-4 这题就是最短路的一种变形,不是求两点之间的最短路,而是求所有路径中的最长边的最小值. 这里还是使用d数组,但是定义不同了,这里的d[i]就是表示从起点到i的路径中最长边中的 ...

  7. HDU1067 Gap

    题目: Let's play a card game called Gap. You have 28 cards labeled with two-digit numbers. The first d ...

  8. SQL-MYSQL的时间格式转换(持续补充)

    ======================SQLSERVER===================================== SELECT CONVERT(varchar(100), GE ...

  9. WDN302国产化网络存储控制模块

    WDN302是一款网络存储控制模块,实现对NAS和IP-SAN的混合支持,通过以太网对海量存储的访问,实现数据的存储.共享.恢复和防丢失. 飞腾 FT1500A/16处理器,主频 1.5GHz: 支持 ...

  10. 冗余网络构建方案对比:VRRP协议、多网卡绑定及WN202冗余链路网卡

    在组建网络时为网络设计冗余方案已经成为提高网络可用性必不可少的一环,伴随着网络技术的发展实现网络冗余的技术方案也是层出不穷,例如应用于服务器端的HA.LB,应用于存储的SAN.DAS.NAS等.本文重 ...