讲多线程前,先要了解什么是进程,什么是线程,已经知道的请略过。

一、进程与线程:

进程是资源分配的最小单位,一个程序至少有一个进程。

线程是程序执行的最小单位,一个进程至少有一个线程。

进程都有自己独立的地址空间,内存,数据栈等,所以进程占用资源多。由于进程的资源独立,所以通讯不方便,只能使用进程间通讯(IPC)。

线程共享进程中的数据,他们使用相同的地址空间,使用线程创建快捷,创建开销比进程小。同一进程下的线程共享全局变量、静态变量等数据,所以线程通讯非常方便,但会存在数据同步与互斥的问题,如何处理好同步与互斥是编写多线程程序的难点。

一个进程中可以存在多个线程,在单核CPU中每个进程中同时刻只能运行一个线程,只有在多核CPU中才能存在线程并发的情况。

当线程需要运行但没有运行空间时,会对线程的优先级进行判断,高优先级先运行,低优先级进程让行。

二、全局解释器锁(GIL)

Python的多线程,只有用于I/O密集型程序时效率才会有明显的提高。

原因如下:

Python代码的执行是由Python虚拟机进行控制。它在主循环中同时只能有一个控制线程在执行,意思就是Python解释器中可以运行多个线程,但是在执行的只有一个线程,其他的处于等待状态。

这些线程执行是有全局解释器锁(GIL)控制,它来保证同时只有一个线程在运行。在多线程运行环境中,Python虚拟机执行方式如下:

  1. 设置GIL
  2. 切换进线程
  3. 执行下面操作之一
  4. 运行指定数量的字节码指令
  5. 线程主动让出控制权
  6. 切换出线程(线程处于睡眠状态)
  7. 解锁GIL
  8. 进入1步骤

注意:Python运行计算密集型的多线程程序时,更倾向于让线程在整个时间片内始终占据GIL,而I/O秘籍型的多线程程序在I/O被调用前会释放GIL,以允许其他线程在I/O执行的时候运行。

三、Python 的 threading 模块

Python 常用的多线程模块有threading 和 Queue,在这里我们将 threading 模块。

threading 模块的Thread 类是主要的执行对象。使用Thread 类,可以有很多方法来创建线程。最常用的有下面三种:

创建Thread 的实例,传给它一个可调用对象(函数或者类的实例方法)。

派生Thread 的子类,并创建子类的实例。

3.1 可调用对象(函数,类的实例方法)使用多线程

步骤如下:



示例:创建Thread实例,传递给他一个函数

from threading import Thread
from time import sleep, ctime def func(name, sec):
print('---开始---', name, '时间', ctime())
sleep(sec)
print('***结束***', name, '时间', ctime()) # 创建 Thread 实例
t1 = Thread(target=func, args=('第一个线程', 1))
t2 = Thread(target=func, args=('第二个线程', 2)) # 启动线程运行
t1.start()
t2.start() # 等待所有线程执行完毕
t1.join() # join() 等待线程终止,要不然一直挂起
t2.join()

示例:创建Thread实例,传递给他一个类的实例方法

from threading import Thread
from time import sleep, ctime class MyClass(object): def func(self,name,sec):
print('---开始---', name, '时间', ctime())
sleep(sec)
print('***结束***', name, '时间', ctime()) def main():
# 创建 Thread 实例
t1 = Thread(target=MyClass().func, args=(1, 1))
t2 = Thread(target=MyClass().func, args=(2, 2)) # 启动线程运行
t1.start()
t2.start() # 等待所有线程执行完毕
t1.join() # join() 等待线程终止,要不然一直挂起
t2.join() if __name__=="__main__":
main()

运行结果:

---开始--- 一 时间 Fri Nov 29 11:34:31 2019
---开始--- 二 时间 Fri Nov 29 11:34:31 2019
***结束*** 一 时间 Fri Nov 29 11:34:32 2019
***结束*** 二 时间 Fri Nov 29 11:34:33 2019

程序总共运行两秒,如果程序按照线性运行需要3秒,节约1秒钟。

Thread 实例化时需要接收 target,args(kwargs)两个参数。

target 用于接收需要使用多线程调用的对象。

args 或 kwargs 用于接收调用对象的需要用到的参数,args接收tuple,kwargs接收dict。

start() 是方法用来启动线程的执行。

join() 方法是一种自旋锁,它用来等待线程终止。也可以提供超时的时间,当线程运行达到超时时间后结束线程,如join(500),500毫秒后结束线程运行。

注意:如果当你的主线程还有其他事情要做,而不是等待这些线程完成,就可以不调用join()。join()方法只有在你需要等待线程完成然后在做其他事情的时候才是有用的。

3.2 派生Thread 的子类,并创建子类的实例。

我们可以通过继承Thread类,派生出一个子类,使用子类来创建多线程。

示例:派生Thread 的子类,传递给他一个可调用对象

from threading import Thread
from time import sleep, ctime # 创建 Thread 的子类
class MyThread(Thread):
def __init__(self, func, args):
'''
:param func: 可调用的对象
:param args: 可调用对象的参数
'''
Thread.__init__(self) # 不要忘记调用Thread的初始化方法
self.func = func
self.args = args def run(self):
self.func(*self.args) def func(name, sec):
print('---开始---', name, '时间', ctime())
sleep(sec)
print('***结束***', name, '时间', ctime()) def main():
# 创建 Thread 实例
t1 = MyThread(func, (1, 1))
t2 = MyThread(func, (2, 2))
# 启动线程运行
t1.start()
t2.start()
# 等待所有线程执行完毕
t1.join()
t2.join() if __name__ == '__main__':
main()

注意:不要忘记在子类中初始化父类的方法Thread.init(self) 。需要重构 run() 方法来执行多线程的程序。

3.3 拓展:获取可调用对象的返回值

在多线程中运行的程序时与主线程分开,我们没法直接获取线程中程序的返回值。这时就可以使用派生Thread 的子类,将给过保存的实例属性中,通过一个新方法获取运行结果。

示例: 获取多线程中程序运行的结果

from threading import Thread
from time import sleep, ctime # 创建 Thread 的子类
class MyThread(Thread):
def __init__(self, func, args):
'''
:param func: 可调用的对象
:param args: 可调用对象的参数
'''
Thread.__init__(self)
self.func = func
self.args = args
self.result = None def run(self):
self.result = self.func(*self.args) def getResult(self):
return self.result def func(name, sec):
print('---开始---', name, '时间', ctime())
sleep(sec)
print('***结束***', name, '时间', ctime())
return sec def main():
# 创建 Thread 实例
t1 = MyThread(func, (1, 1))
t2 = MyThread(func, (2, 2))
# 启动线程运行
t1.start()
t2.start()
# 等待所有线程执行完毕
t1.join()
t2.join()
# 或线程中程序的运行结果
print(t1.getResult())
print(t2.getResult()) if __name__ == '__main__':
main()

补充1:threading 模块的类与函数

  1. threading 模块的类对象
Thread 执行线程
Timer 在运行前等待一段时间的执行线程
Lock 原语锁(互斥锁,简单锁)
RLock 重入锁,使单一线程可以(再次)获得已持有的锁
Condition 条件变量,线程需要等待另一个线程满足特定条件
Event 事件变量,N个线程等待某个事件发生后激活所有线程
Semaphore 线程间共享资源的寄存器
BoundedSemaphore 与Semaphore 相似,它不允许超过初始值
Barrie 执行线程达到一定数量后才可以继续
  1. threading 模块的函数
activeCount() 获取当前活动中的Thread对象个数
currentThread() 获取当前的Thread对象
enumerate() 获取当前活动的Thread对象列表
settrace(func) 为所有线程设置一个跟踪(trace)函数
setprofile(func) 为所有线程设置配置文件(profile)函数
stack_size(size=None) 获取新创建线程的栈大小,也可设置线程栈的大小为size。

补充2 :Thread 类的属性与方法

threading 中的 Thread 类是主要的执行对象,主要方法和属性如下:

属性

name 线程名称
ident 线程标识符号
daemon 是否为守护线程

方法

___init__(self, group=None, target=None, name=None, args=(), kwargs=None, *, daemon=None)
参数:group 无用,保留参数
target 可调用的目标
name 线程的名称
args,kwargs 调用目标的参数
daemon 是否为守护线程
start() 开始执行
join(timeout=None) 阻塞timeout秒,否则直到启动的线程终止前一直挂起
is_alive () 线程是否存活
isDaemon() 是否为守护线程
setDaemon(daemonic) 设置为守护线程

来源:https://zhuanlan.zhihu.com/p/91601448

Python threading实现多线程 基础篇的更多相关文章

  1. 智普教育Python视频教程之入门基础篇,python笔记

    智普教育Python视频教程之入门基础篇,python笔记 print id()内存地址 type()变量类型 windows命令行下edit命令 python数据类型不需要指定类型 定义hostna ...

  2. Python(三)基础篇之「模块&面向对象编程」

    [笔记]Python(三)基础篇之「模块&面向对象编程」 2016-12-07 ZOE    编程之魅  Python Notes: ★ 如果你是第一次阅读,推荐先浏览:[重要公告]文章更新. ...

  3. Python(四)基础篇之「文件对象&错误处理」

    [笔记]Python(四)基础篇之「文件对象&错误处理」 2016-12-08 ZOE    编程之魅  Python Notes: ★ 如果你是第一次阅读,推荐先浏览:[重要公告]文章更新. ...

  4. Python学习笔记之基础篇(-)python介绍与安装

    Python学习笔记之基础篇(-)初识python Python的理念:崇尚优美.清晰.简单,是一个优秀并广泛使用的语言. python的历史: 1989年,为了打发圣诞节假期,作者Guido开始写P ...

  5. python——线程与多线程基础

    我们之前已经初步了解了进程.线程与协程的概念,现在就来看看python的线程.下面说的都是一个进程里的故事了,暂时忘记进程和协程,先来看一个进程中的线程和多线程.这篇博客将要讲一些单线程与多线程的基础 ...

  6. 【新手学Python】一、基础篇

    由于以前处理数据用Matlab和C,最近要处理大量文本文件,用C写实在是太繁琐,鉴于Python的强大文本处理能力,以及其在Deep Learning上有着很大优势,本人打算从即日起学习Python, ...

  7. Python高频技巧总结[基础篇]

    0. 概要说明 python应用最多的场景还是web快速开发.爬虫.自动化运维:简单网站.自动Fuzz脚本.收发邮件脚本.简单验证码识别脚本. 爬虫在开发过程中也有很多复用的过程,这里总结一下,以后也 ...

  8. Python学习总结之一 -- 基础篇

    Python学习第一篇 一:写在前面 啊,最近我的新博客一直都没有更新学习内容了,只是最近一直都在忙着寻找实习机会(或许这只是一个借口,真实原因是我太懒惰了,改改改!).终于今天又投递了几个新的实习职 ...

  9. 【Python系统学习】基础篇

    这次真的是最后一次了!第三次滚Python的基础.走了太多弯路.认真一点!菜鸟! 教程 转义字符 \ 可以转义很多字符,比如\n表示换行,\t表示制表符,字符\本身也要转义,所以\\表示的字符就是\ ...

  10. Python 学习笔记(基础篇)

    背景:今年开始搞 Data science ,学了 python 小半年,但一直没时间整理整理.这篇文章很基础,就是根据廖雪峰的 python 教程 整理了一下基础知识,再加上自己的一些拓展,方便自己 ...

随机推荐

  1. golang的 CGO 是什么

    CGO是Go(Golang)语言中的一个工具,全称为 "C-Go" 或者 "C for Go". 它是Go标准库的一部分,允许Go代码与C语言代码进行交互. C ...

  2. 008. gitlab代码克隆与推送

    推送配置 gitlab需要推送的客户端sshkey添加到gitlab服务器中 node1 推送配置 centos node1: [root@node1 ~]# cat ~/.ssh/id_rsa.pu ...

  3. 003. git标签

    git标签 标签用于记录详细修改内容,为后续使用提供帮助. 也方便快速的回滚. 每一次提交[commit]都可以打一个tag标签. 测试阶段标签规则: 以 0.01 0.02版本为主 正式上线标签规则 ...

  4. iOS 后台录音Tweak实现参考--stackoverflow

    up vote9down voteaccepted +50 "Audio Recorder" is indeed a very simple tweak. The author t ...

  5. Qt开发技术:Q3D图表开发笔记(四):Q3DSurface三维曲面图颜色样式详解、Demo以及代码详解

    前言   qt提供了q3d进行三维开发,虽然这个框架没有得到大量运用也不是那么成功,性能上也有很大的欠缺,但是普通的点到为止的应用展示还是可以的.  其中就包括华丽绚烂的三维图表,数据量不大的时候是可 ...

  6. .NET 8 IApplicationBuilder详解

    在上节中我们已经得知 WebApplication 实现了 IApplicationBuilder,我们浅谈了其pipe特质和构建方法,本节中将深入了解 ApplicationBuilder 以窥探 ...

  7. 操作标签的属性和属性值 table表格

     // 操作标签的属性和属性值         // 特殊的属性           // 可以直接通过 点语法或者[]语法来操作的属性         // id 和 class           ...

  8. 架构与思维:了解Http 和 Https的区别(图文详解)

    1 介绍 随着 HTTPS 的不断普及和使用成本的下降,现阶段大部分的系统都已经开始用上 HTTPS 协议. HTTPS 与 HTTP 相比, 主打的就是安全概念,相关的知识如 SSL .非对称加密. ...

  9. 太卷了,史上最简单的监控系统 catpaw 简介

    指标监控的痛点 当下比较流行的监控系统,比如 Prometheus.Nightingale.VictoriaMetrics,都是基于数值型指标的监控系统,这类监控系统的痛点在于:告警的时候只能拿到异常 ...

  10. leetcode-3-无重复字符的最长子串-javascript

    题目 给定一个字符串,请你找出其中不含有重复字符的 最长子串 的长度. 示例 1: 输入: "abcabcbb" 输出: 3 解释: 因为无重复字符的最长子串是 "abc ...