线程

线程(Thread)也叫轻量级进程,是操作系统能够进行运算调度的最小单位,它被包含在进程之中,是进程中的实际运作单位。线程自己不拥有系统资源,只拥有一点在运行中必不可少的资源,但它可与同属的一个进程的其它线程共享进程所拥有的全部资源。一个线程可以创建和撤销另一个线程,同一进程中的多个线程之间可以并发执行。

多线程

多线程是一个可以提高程序运行效率的方法。一些按顺序执行的程序可以使用多线程实现并行执行,从而提高整体效率。然而,多线程也不是可以提升所有程序的执行效率。执行的程序分为CPU密集型和I/O密集型两种,多线程技术比较适用于后者。

因为在串行结构中读写磁盘或者进行网络通信的时候CPU是闲着的,毕竟网络比磁盘要慢几个数量级,磁盘比内存要慢几个数量级,内存又比CPU慢几个数量级。

比如你使用单线程对多个网站发送请求,如果这多个网站中的其中2、3个网站的响应速度很慢,而其他网站需要等待这几个网站加载完成才发送请求,这样就会影响了整体的效率。而使用多线程并发请求这多个网站,就可以在那些响应较慢的网站加载的同时去请求其他网站,大大提升整个程序的运行效率。

GIL锁

GIL的全称是Global Interpreter Lock(全局解释器锁),Python最初的设计理念在于,为了解决多线程之间数据完整性和状态同步的问题,设计为在任意时刻只能由一个线程在解释器中运行。因此Python中的多线程是表面上的多线程(同一时刻只有一个线程),不是真正的多线程。

真正意义上的多线程是由CPU来控制的,Python中的多线程是由GIL控制的。如果一个CPU密集型的程序,用C语言写,运行在一个四核处理器上,采用多线程的话最多可以获得4倍的效率提升。但是用Python写的话,效率不会提高,甚至会变慢,因为除了程序本身执行外,还多了线程切换所花的时间。

因此,Python多线程相对更适合写I/O密集型的程序,真正对效率要求高的CPU密集型程序都用C/C++去写。

Python多线程

在Python中,我们一般使用threading模块来实现多进程操作。

threading模块中包含了关于线程操作的丰富功能,包括:常用的线程函数、线程对象、锁对象、递归锁对象、事件对象、条件变量对象、信号量对象、定时器对象、栅栏对象。

threading.Thread

我们使用threading模块中的Thread类来创建线程对象

threading.Thread(group=None, target=None, name=None, args=(), kwargs={}, *, daemon=None)

下面是Thread的参数说明

group:默认为None(该参数是为了以后实现ThreadGroup类而保留的)

target:在run方法中调用的可调用对象,即需要开启线程的可调用对象,比如函数或方法。

name:线程名称,默认为“Thread-N”形式的名称,N为较小的十进制数。

args:在参数target中传入的可调用对象的参数元组,默认为空元组()。

kwargs:在参数target中传入的可调用对象的关键字参数字典,默认为空字典{}。

daemon:默认为None,即继承当前调用者线程(即开启线程的线程,一般就是主线程)的守护模式属性,如果不为None,则无论该线程是否为守护模式,都会被设置为“守护模式”。

线程对象的一些重要方法

start():开启线程活动。它将使得run()方法在一个独立的控制线程中被调用,需要注意的是同一个线程对象的start()方法只能被调用一次,如果调用多次,则会报RuntimeError错误。

run():此方法代表线程活动。

join(timeout=None):让当前调用者线程(一般为主线程)等待,直到线程结束。

daemon:表示该线程是否为守护线程,True或者False。设置一个线程的daemon必须在线程的start()方法之前,否则会报RuntimeError错误。这个值默认继承自创建它的线程,主线程默认是非守护线程,所以在主线程中创建的线程默认都是非守护线程的,即daemon=False。

直接创建

import time

import random

import threading

def func(name):

s = random.randint(1, 5)

print(f'current thread is {name}, sleeping {s}s.')

time.sleep(s)

print(f'thread {name} is over')

if __name__ == '__main__':

for i in range(1, 5):

t = threading.Thread(target=func, args=(i,))

t.start()

print('Main Thread')

结果如下

current thread is 1, sleeping 5s.

current thread is 2, sleeping 3s.

current thread is 3, sleeping 3s.

current thread is 4, sleeping 2s.

Main Thread

thread 4 is over

thread 2 is over

thread 3 is over

thread 1 is over

上面例子开启了4个线程,4个线程并发执行任务,先完成任务的线程先输出结果。

继承创建

import time

import random

import threading

class Func(threading.Thread):

def __init__(self, name):

super().__init__()

self.name = name

def run(self):

s = random.randint(1, 5)

print(f'current thread is {self.name}, sleeping {s}s.')

time.sleep(s)

print(f'thread {self.name} is over')

if __name__ == '__main__':

for i in range(1, 5):

t = Func(str(i))

t.start()

print('Main Thread')

结果如下

current thread is 1, sleeping 3s.

current thread is 2, sleeping 4s.

current thread is 3, sleeping 3s.

current thread is 4, sleeping 5s.

Main Thread

thread 1 is over

thread 3 is over

thread 2 is over

thread 4 is over

线程对象的start()实际上调用了Func类中的run()方法来开启一个线程。

join方法的简单示例

在第一个例子的基础上加入join方法

import time

import random

import threading

def func(name):

s = random.randint(1, 5)

print(f'current thread is {name}, sleeping {s}s.')

time.sleep(s)

print(f'thread {name} is over')

if __name__ == '__main__':

print('Main Thread start')

tlist = []

for i in range(1, 5):

t = threading.Thread(target=func, args=(i,))

t.start()

tlist.append(t)

for t in tlist:

t.join()

print('do something')

print('Main Thread over')

结果如下

Main Thread start

current thread is 1, sleeping 4s.

current thread is 2, sleeping 1s.

current thread is 3, sleeping 5s.

current thread is 4, sleeping 1s.

thread 2 is over

thread 4 is over

thread 1 is over

thread 3 is over

do something

Main Thread over

先开启所有子线程,然后放到线程列表tlist中,在对每一个线程调用join()方法来进行阻塞,直到每个子线程执行完毕后,才会继续执行主线程后面的代码。

Python线程池

系统启动一个新线程的成本是很高的,因为它涉及与操作系统的交互。在这种情况下,使用线程池可以很好地提升性能,尤其是当程序中需要创建大量生存期很短暂的线程时,更应该考虑使用线程池。

线程池在系统启动时即创建大量空闲的线程,程序只要将一个函数提交给线程池,线程池就会启动一个空闲的线程来执行它。当该函数执行结束后,该线程并不会死亡,而是再次返回到线程池中变成空闲状态,等待下一个函数。

此外,使用线程池可以有效地控制系统中并发线程的数量。当系统中包含有大量的并发线程时,会导致系统性能急剧下降,甚至导致Python解释器崩溃,而线程池的最大线程数参数可以控制系统中并发线程的数量不超过此数。

python线程池的使用

在过去,我们可以使用第三方模块threadpool来创建线程池。但是现在主流的使用线程池的模块是python3中自带的模块concurrent.futures模块中的ThreadPoolExecutor,如果对threadpool感兴趣的小伙伴可以自行搜索相关的信息。

下面主要介绍一下ThreadPoolExecutor的使用方法。

import time

import random

from concurrent.futures import ThreadPoolExecutor

def func(name):

s = random.randint(1, 5)

print(f'current thread is {name}, sleeping {s}s.')

time.sleep(s)

print(f'thread {name} is over')

if __name__ == '__main__':

with ThreadPoolExecutor(max_workers=3) as t:

for i in range(1, 6):

t.submit(func, i)

结果如下

current thread is 1, sleeping 1s.

current thread is 2, sleeping 1s.

current thread is 3, sleeping 2s.

thread 1 is over

current thread is 4, sleeping 2s.

thread 2 is over

current thread is 5, sleeping 4s.

thread 3 is over

thread 4 is over

thread 5 is over

创建一个最大容纳数量为3的线程池对象t,通过submit提交执行的函数到线程池中,但线程池中的某个线程(thread 1)执行完成,则把空闲的线程(thread 4)放入到池子中,直到所有线程执行完成则程序结束。

python多线程并发_Python并发之多线程相关推荐

  1. python多线程并发_Python进阶记录之基础篇(二十四)

    回顾 在Python进阶记录之基础篇(二十三)中,我们介绍了进程的基本概念以及Python中多进程的基本使用方法.其中,需要重点掌握多进程的创建方法.进程池和进程间的通信.今天我们讲一下Python中 ...

  2. python paramiko并发_python学习笔记9--paramiko模块、多线程、锁机制

    一.paramiko模块 paramiko模块是一个遵循ssh2协议的python扩展模块,该模块可以允许使用python通过ssh协议去远程管理主机.在使用该模块前,需要手动安装,具体安装过程请百度 ...

  3. python多线程并发每秒6000_Python多线程并发的误区

    由于项目要做一个并发测试,由于断言的东西较多,决定手写脚本.于是用python写了脚本: def test_method(thread_no): print("%s===test_metho ...

  4. python diango 并发_python - django Model 并发写数据出现重复值

    问 题 view视图代码: @login_required def data(request, page, keyword,strEncode): current_username = request ...

  5. python paramiko并发_python paramiko 多线程批量执行指令及批量上传文件和目录

    源代码: 环境需求: 1.python3 2.paramiko pip install --upgrade pip apt-get install libssl-dev pip3 install pa ...

  6. python爬虫多线程下载_Python爬虫之多线程下载豆瓣Top250电影图片

    爬虫项目介绍 本次爬虫项目将爬取豆瓣Top250电影的图片,其网址为:https://movie.douban.com/top250, 具体页面如下图所示: 本次爬虫项目将分别不使用多线程和使用多线程 ...

  7. python多线程没用_Python中的多线程cv2.imshow()不起作用

    我有两个摄像头(使用OpenNI,每个摄像头有两个流,由相同的驱动程序API实例处理),并且想要两个线程,每个线程捕获数据从每个摄像机独立,即驱动程序API的一个实例,说cam_handler,我有两 ...

  8. python任务队列框架_Python实现简单多线程任务队列

    def gradient_descent(): # the gradient descent code plotly.write(X, Y) 一般来说,当网络请求 plot.ly 绘图时会阻塞等待返回 ...

  9. python 多进程并发_python并发编程之多进程

    一 multiprocessing模块介绍 python中的多线程无法利用多核优势,如果想要充分地使用多核CPU的资源(os.cpu_count()查看),在python中大部分情况需要使用多进程.P ...

最新文章

  1. python dash库_让你事半功倍的小众 Python 库
  2. 【学术相关】你只看到了200万年薪的招聘,看不到被困校园的几十万博士
  3. C语言烧写C51单片机的线,51单片机烧写程序过程以及详细说明【图文】
  4. jasmine没有调用fixture.detectChanges就取不到元素的原因
  5. JAVA WEB篇3——JSP
  6. 重新安装python2.6 和 yum (不可以直接安装yum yum 依赖于python2.6)
  7. 特斯拉AI Day首秀:FSD终极进化?AI超算Dojo、D1芯片、人形机器人亮相!
  8. 【转】Qt编写串口通信程序全程图文讲解
  9. MQTT 连接服务端失败,报错客户机未连接(32104)
  10. Spring IOC源码笔记(二)
  11. 2款免费的图片压缩工具
  12. SQL数据分析之数据提取、数据查询、数据清洗【MySQL速查】
  13. 关于android studio报错Attempt to invoke virtual method 'void android.widget.ListView.setAdapter(android.
  14. 图解TCPIP---第一章
  15. 机器学习之邹博笔记1
  16. R绘图笔记 | 生存曲线的绘制
  17. 微信订阅出现errMsg“:“requestSubscribeMessage:fail can only be invoked by user TAP gesture 解决方案
  18. 03-页面布局[Python]
  19. 【编程学习】浅谈哈希表及用C语言构建哈希表!
  20. 学前教育试题库及答案_(完整版)学前教育学试题和答案

热门文章

  1. 微信小程序 刻度计/温度计组件(自用)
  2. Selenium使用xpath定位元素
  3. 你知道如何生成随机数吗?(超详细附图)
  4. Error: rpmdb open failed 使用 yum进行安装时,出现这个错误的解决方式
  5. matplotlib-annotate
  6. 关系数据库(数据库原理)
  7. Android入门(一)——结构
  8. 每日邮报:灵长动物传授5条职场法则
  9. C语言标准时间转时间戳
  10. 【微信小程序】利用MPFlutter开发微信小程序