python framework threads_Python - 多线程

当我们写的程序需要并发时,我们就需要用到 Python 中的一些并发库,例如 asyncio、thread、 multiprocessing 等,本文主要介绍 Python 标准库中的多线程库 thread

threading 基本使用

使用多线程的优势在于

程序运行更快

适用于 IO 密集的场景

Python 标准库提供了两个模块,_thread 和 threading ,threading 对 _thread 进行了封装,虽然 Python 有 GIL ,会在线程切换时消耗很多资源,但是在 IO 密集的场景下,Python 多线程还是很管用的

先看看 threading 的基本使用

import threading

def hello(*args, **kwargs):#定义一个 hello 函数

print('hello, world', args, kwargs)

实例化线程对象,target 参数为指定函数,args 是传递的列表参数,kwargs 是传递的字典参数,通过 start 方法启动一个线程

>>>t = threading.Thread(target=hello, args=[1, 2, 3], kwargs={'a': 'b'})

>>>t.start()

hello, world (1, 2, 3) {'a': 'b'}

threading 和 Thread 常用参数和方法

name 参数

导入 logging 库,更加直观的显示线程的信息

import logging

logging.basicConfig(level=logging.DEBUG, format='%(asctime)s %(levelname)s [%(threadName)s] %(message)s') #配置 logging

def hello():

logging.info('test')

>>>t = threading.Thread(target=hello, name='hello_thread')#指定线程名称

2017-03-12 22:30:58,556 INFO [hello_thread] test

daemon

线程退出的时候,该线程的所有daemon 的子线程都会退出,而no-daemon 子线程不会退出

而线程退出会等待所有的 no-daemon 子线程退出

join 方法

子线程的 join 方法会阻塞主线程,直到子线程退出或者超时,如果不设置超时时间,就会一直等待子线程退出

import threading

from time import sleep

def worker():

sleep(3)

threads = [threading.Thread(target=worker) for _ in range(3)]

def main():

for t in threads:

t.start()

for t in threads:

t.join()

执行 main 函数能够感觉到是一直阻塞着的,直到子线程退出

>>>main()

enumerate 方法

列出当前所有的线程

>>>threading.enumerate()

[<_mainthread started>,

local

线程共享内存、状态和资源,但是threading local 对象的属性,只对当前线程可见

>>>ctx = threading.local()

>>>ctx

>>>ctx.data = 'aaa'

def worker():

print(ctx.data)

>>>worker()

'aaa'

>>>threading.Thread(target=worker).start()

In [101]: Exception in thread Thread-2477:

Traceback (most recent call last):

File "/usr/local/opt/pyenv/versions/3.5.3/Python.framework/Versions/3.5/lib/python3.5/threading.py", line 914, in _bootstrap_inner

self.run()

File "/usr/local/opt/pyenv/versions/3.5.3/Python.framework/Versions/3.5/lib/python3.5/threading.py", line 862, in run

self._target(*self._args, **self._kwargs)

File "", line 2, in worker

print(ctx.data)

AttributeError: '_thread._local' object has no attribute 'data'

实例化 Thread 类

之前通过 target 参数的方式不是非常的优雅,其实可以通过继承 Thread 类并重写 run 方法来编写更加优雅的代码

class MyThread(threading.Thread):

def run(self):

print('hello, world')

>>>MyThread()

>>>MyThread().start()

hello, world

传递参数

通过重写 __init__() 方法传递参数

class MyThread(threading.Thread):

def __init__(self, *args, **kwargs):

super().__init__()

self.args = args

self.kwargs = kwargs

def run(self):

print('hello, world', self.args, self.kwargs)

>>>t = MyThread(1, 2, 3, state='ok')

>>>t.start()

hello, world (1, 2, 3) {'state': 'ok'}

线程同步

在使用多个线程同时操作一个资源的情况下( 例如读文件) ,我们需要控制同一时刻只有一个线程对资源进行操作,这时候就需要一些同步机制,如 锁、队列、条件、事件等

Lock

我们可以通过 threading.Lock 来解决这个问题

Lock 对象一般有两个操作,获取 acquire 和 释放 release

通过 acquire 方法 将 Lock 对象状态设置为锁定,如果是锁定状态则会阻塞,release 方法则将 Lock 对象解锁

import threading

lock = threading.Lock()

>>>lock.acquire()

True

>>>lock.acquire(timeout=3)

False

>>>lock.release()

>>>lock.acquire(timeout=3)

True

一个抓取页面的例子,通过使用锁,实现了线程之间的同步

import requests

import threading

import logging

logging.basicConfig(level=logging.INFO, format='%(asctime)s %(levelname)s [%(threadName)s] %(message)s')

lock = threading.Lock()

file = open('data', 'a')

urls = ['http://baidu.com', 'http://cn.bing.com/']

class FetchUrls(threading.Thread):

def __init__(self, url: str, file, lock: threading.Lock, name=None):

super().__init__()

self.url = url

self.file = file

self.lock = lock

if name is not None:

self.name = name

def run(self):

res = requests.get(self.url)

self.lock.acquire()

logging.info('Lock Acquire')

self.file.write(res.text)

logging.info('File Writed')

self.lock.release()

测试

>>>ts = [FetchUrls(url, file, lock, name=url) for url in urls]

>>>[t.start() for t in ts]

2017-03-13 14:00:05,142 INFO [http://baidu.com] Lock Acquire

2017-03-13 14:00:05,142 INFO [http://baidu.com] File Writed

2017-03-13 14:00:05,271 INFO [http://cn.bing.com/] Lock Acquire

2017-03-13 14:00:05,272 INFO [http://cn.bing.com/] File Writed

RLock

RLock 是一个可重用锁,可以多次调用 acquire 而不阻塞,但是 release 时也要执行和 acquire 一样的次数

import threading

>>>rlock = threading.RLock()

>>>rlock.acquire()

True

>>>rlock.acquire()

True

>>>rlock.acquire()

True

>>>rlock.release()

>>>rlock.release()

>>>rlock.release()

>>>rlock.release()

Traceback (most recent call last):

File "", line 1, in

RuntimeError: cannot release un-acquired lock

Condition

如果多个线程使用 生产者 —> 消费者的模式,可以使用 Condition,生产者生产数据后,通过 notify/notify_all 通知给消费者消费数据

import threading

import random

import logging

import time

logging.basicConfig(level=logging.INFO, format='%(asctime)s %(levelname)s [%(threadName)s] %(message)s')

class Producer(threading.Thread):

def __init__(self, datas: list, cond: threading.Condition, name=None):

super().__init__()

self.cond = cond

self.datas = datas

if name is not None:

self.name = name

def run(self):

while True:

data = random.randint(1, 100)

logging.info(data)

self.datas.append(data)

self.cond.acquire()

self.cond.notify()

self.cond.release()

time.sleep(1)

"""

self.cond.acquire()

self.cond.notify()

self.cond.release()

等价于

with self.cond:

self.notify()

无论 notify 还是 wait 都需要先 acquire,然后再 release

一般使用 with 语句

"""

class Consumer(threading.Thread):

def __init__(self, datas: list, cond: threading.Condition, name=None):

super().__init__()

self.cond = cond

self.datas = datas

if name is not None:

self.name = name

def run(self):

while True:

self.cond.acquire()

while True:

data = self.datas.pop()

logging.info(data)

break

self.cond.wait() #消费者通过 wait 方法等待 生产者 notify

self.cond.release()

def runner():

datas = []

cond = threading.Condition()

t1 = Producer(datas, cond, name='producer')

t2 = Consumer(datas, cond, name='consumer')

t1.start()

t2.start()

t1.join()

t2.join()

测试

>>>runner()

2017-03-13 14:56:12,442 INFO [producer] 89

2017-03-13 14:56:12,442 INFO [consumer] 89

2017-03-13 14:56:13,445 INFO [producer] 85

2017-03-13 14:56:13,445 INFO [consumer] 85

2017-03-13 14:56:14,450 INFO [producer] 57

2017-03-13 14:56:14,450 INFO [consumer] 57

2017-03-13 14:56:15,454 INFO [producer] 65

2017-03-13 14:56:15,454 INFO [consumer] 65

2017-03-13 14:56:16,458 INFO [producer] 15

2017-03-13 14:56:16,459 INFO [consumer] 15

Event

Event 是一个简单的机制,线程发出一个信号,其他线程等待

import threading

import logging

import time

import random

logging.basicConfig(level=logging.INFO, format='%(asctime)s %(levelname)s [%(threadName)s] %(message)s')

class EventProducer(threading.Thread):

def __init__(self, datas: list, event: threading.Event, name=None):

super().__init__()

self.datas = datas

self.event = event

if name is not None:

self.name = name

def run(self):

while True:

data = random.randint(1, 100)

logging.info(data)

self.datas.append(data)

self.event.set()

time.sleep(1)

class EventConsumer(threading.Thread):

def __init__(self, datas: list, event: threading.Event, name=None):

super().__init__()

self.datas = datas

self.event = event

if name is not None:

self.name = name

def run(self):

while True:

self.event.wait() # wait 方法阻塞 消费者线程

try:

data = self.datas.pop()

logging.info(data)

except IndexError:

continue

def runner():

event = threading.Event()

datas = []

t1 = EventProducer(datas, event, name='EventProducer')

t2 = EventConsumer(datas, event, name='EventConsumer')

t1.start()

t2.start()

t1.join()

t2.join()

测试

>>>runner()

2017-03-13 16:18:54,251 INFO [EventProducer] 82

2017-03-13 16:18:54,251 INFO [EventConsumer] 82

2017-03-13 16:18:55,261 INFO [EventProducer] 37

2017-03-13 16:18:55,261 INFO [EventConsumer] 37

2017-03-13 16:18:56,270 INFO [EventProducer] 92

2017-03-13 16:18:56,271 INFO [EventConsumer] 92

Queue

之前的几个 提供者 —> 消费者 的例子 一直用一个全局的列表来传递数据,其实不是很科学,不同线程传递数据应该使用 Queue ,因为 Queue 本身也可以阻塞线程,使用 Queue 还可以省去同步

import queue

import threading

import logging

import random

from time import sleep

logging.basicConfig(level=logging.INFO, format='%(asctime)s %(levelname)s [%(threadName)s] %(message)s')

class QueueProducer(threading.Thread):

def __init__(self, queue: queue.Queue(), name=None):

super().__init__()

self.queue = queue

if name is not None:

self.name = name

def run(self):

while True:

data = random.randint(1, 100)

logging.info(data)

self.queue.put(data)

sleep(1)

class QueueConsumer(threading.Thread):

def __init__(self, queue: queue.Queue, name=None):

super().__init__()

self.queue = queue

if name is not None:

self.name = name

def run(self):

while True:

data = self.queue.get()

logging.info(data)

def runner():

q = queue.Queue()

t1 = QueueProducer(q, name='QueueProducer')

t2 = QueueConsumer(q, name='QueueConsumer')

t1.start()

t2.start()

t1.join()

t2.join()

测试

>>>runner()

2017-03-13 16:34:49,401 INFO [QueueProducer] 82

2017-03-13 16:34:49,401 INFO [QueueConsumer] 82

2017-03-13 16:34:50,405 INFO [QueueProducer] 2

2017-03-13 16:34:50,405 INFO [QueueConsumer] 2

2017-03-13 16:34:51,406 INFO [QueueProducer] 74

GIL

提到 Python 多线程就一定要说说 GIL Global Interpreter Lock 全局解释器锁,由于 GIL 的存在,Python 的线程不能达到真正的并行,在 CPython (C语言实现的 Python) 中 线程使用的是操作系统原生的线程

CPython 中,一个解释器有一条主线程,和若干条用户程序的线程,由于 GIL 的存在,每一个进程要执行时,都要去获取 GIL ,所以并不能有效的利用多核 CPU 实现多线程并行,也就是说,多个线程不能够同时执行

如果要实现真正的并行,就需要使用 multiprocessing 这个多进程模块了

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/526910.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

计算机关机键桌面,电脑桌面按钮关机关不了怎么办? 爱问知识人

【问题描述】:屏幕抖动,显示异常【原因分析】:1。 显卡驱动异常2。 显示器和主机的连接线虚接3。 显示器本身出现故障【简易步骤】:1。打开【60软件管家】—【软件卸载】2。 【我的电脑】—右击—【管理】—【设备管理器】—右键【卸载】【操作步骤】:方案一&#xff1a;卸载显…

如何快速的学习html5,高效快速学习HTML5的技巧

HTML5是非常新前端必备技能&#xff0c;作为第五代HTML语言&#xff0c;包含的算法、实现的功能都大大超过从前。不同的人学习效率、成果都各不相同&#xff0c;那么如何高效快速学习HTML5呢&#xff1f;学码思前端开发培训老师给大家分享一些小技巧&#xff0c;一起来看看吧。…

java格式化日期时分秒_java怎么格式化时间类型的时间,结果要求是时间类型

你好小SongSimpleDateFormat sdf new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");1.Calendar 转化 String//获取当前时间的具体情况,如年,月,日,week,date,分,秒等Calendar calendat Calendar.getInstance();SimpleDateFormat sdf new SimpleDateFormat("yyyy…

云南大学计算机网络期中考试刘春花,实验九基于CRC编码的检错程序的实现.doc...

实验九基于CRC编码的检错程序的实现.doc云南大学软件学院实 验 报 告课程 计算机网络原理实验 任课教师 刘春花&#xff0c;刘宇 姓名 学号 专业 成绩 实验九、链路层实验基于 CRC 编码的检错程序的实现实验报告一、实验目的1、通过实验&#xff0c;掌握 CRC 编码和解码的原理。…

分享到facebook没有封面图_拾柒自制书封面图分享~

最近大家都在写书 我自己写完收集了很多图片和文字素材现在拿出来分享给大家~希望你们可以用的到分享几张时候做封面的图片给大家哈&#xff01;还有特别多文字素材 大家需要也可以分享给大家呀&#xff01;祝大家都能幸福快乐❤️

桌面计算机盖帘,一种可调节高度的计算机显示器支架的制作方法

本实用新型涉及计算机硬件领域&#xff0c;尤其是一种可调节高度的计算机显示器支架。背景技术&#xff1a;随着科技的进步&#xff0c;社会的发展&#xff0c;人们的生活水平得到了很大的提高&#xff0c;计算机对人们已不再陌生&#xff0c;几乎每家必备一台。通常情况下&…

win10软件拒绝访问删不掉_Win10右键菜单添加“获取文件管理员权限”选项

文件访问被拒绝&#xff0c;你需要TrustedInstaller提供的权限才能对此文件进行更改/文件访问被拒绝&#xff0c;你需要权限才能执行此操作/你需要提供管理员权限才能移动此文件”两种方案一长期为Win10右键菜单添加“获取文件管理员权限”选项 - Windows10.Pro​www.windows10…

计算机网络主观论述题,《计算机网络》论述题

匿名用户1级2018-11-15 回答1&#xff1a;时分多路复用为了提高线路利用率&#xff0c;总是设法在一堆传输线路上&#xff0c;传输多个话路的信息&#xff0c;这就是多路复用。多路复用通常有频分制、时分制和码分制三种。频分制是将传输频带分成N部分&#xff0c;每一个部分均…

db2中null和空值的区别_MySQL数据库的表中 NULL 和 空值 到底有什么区别呢?

作者&#xff1a;极客小俊 一个专注于web技术的80后我不用拼过聪明人&#xff0c;我只需要拼过那些懒人 我就一定会超越大部分人!知乎极客小俊&#xff0c;官方首发原创文章浅谈 NULL 和 空值的区别NULL也就是在字段中存储NULL值空字符串值也就是字段中存储空字符()我们来通过测…

计算机视觉实验数学形态学,计算机视觉形态学边缘检测.doc

实验二 形态算子一、实验目的与要求目的&#xff1a;学习常见的数学形态学运算基本方法&#xff0c;了解腐蚀、膨胀、开运算、闭运算取得的效果&#xff0c;培养处理实际图像的能力。要求&#xff1a;上机运行&#xff0c;调试通过。二、 实验设备&#xff1a;计算机、Matlab软…

新联会是什么组织的作用_“新国标”脚蹬子受到人们热议!它的作用是什么,可有可无吗?...

在“新国标”实施的这段时间里&#xff0c;有很多人都开始正常出行&#xff0c;人们也不再为电动车而烦恼。因为你要骑行老国标电动车&#xff0c;那么你就去上牌即可。如果你想体验“新国标”电动车&#xff0c;那么在你购买的时候也会给你上牌&#xff0c;那么上路是没有任何…

南航计算机学院岳涛,自动化学院 - 南京航空航天大学

发表论文&#xff1a;[1]MinYao,Min Zhao.Developing an improved MPC algorithm for Dynamic UAV PathPlanning Avoiding Moving Threat.Robotica,2015,33(3)611-621.SCI[2]Yao Min ,ZhaoMin.CooperativeAttackStrategy of Unmanned Aerial Vehicles in Adversarial Environme…

server精简版代理意外终止 sql_来自阿里巴巴内部JAVA面试宝典意外流出

前言来自阿里巴巴内部面试宝典意外流出&#xff01;面试宝典内容(JVMMybatisZookeeper)&#xff0c;附答案解析MyBatis系列面试宝典1.MyBatis是什么&#xff1f;Mybatis 是一个半 ORM(对象关系映射)框架&#xff0c;它内部封装了 JDBC&#xff0c;开发时只需要关注 SQL 语句本身…

天津科技大学计算机网络,计算机网络PPT(天津科技大学讲稿-张强)第一章英文对照...

计算机网络课程讲义Chapter 1 IntroductionA note on the use of these ppt slides:We’re making these slides freely available to all (faculty, students, readers). They’re in PowerPoint form so you can add, modify, and delete slides (including this one) and sl…

sox处理mp3_音频处理常用Linux命令总结(一)

今天总结一下常用的linux音频处理工具soX - Sound eXchangesoxisoxi 常用选项soxi: SoX vUsage: soxi [-V[level]] [-T] [-t|-r|-c|-s|-d|-D|-b|-B|-p|-e|-a] infile1 ...-V[n] Increment or set verbosity level (default is 2)-T With -s, -d or -D, display the tota…

2019年12月计算机统考答案,2019年12月计算机等级WPS考前冲刺练习题及答案

【导语】2019年12月计算机等级考试已经进入冲刺阶段&#xff0c;为了方便考生及时有效的备考&#xff0c;那么&#xff0c;无忧考网为您精心整理了2019年12月计算机等级WPS考前冲刺练习题及答案&#xff0c;把握机会抓紧练习吧。如想获取更多计算机等级考试的模拟题及备考资料&…

七乐彩中奖规则表_【开奖】双色球第2020094期开奖结果出炉!你中奖了吗?

东莞福彩——让您人生多福&#xff0c;生活多彩&#xff01;购福彩&#xff0c;做公益&#xff0c;圆梦想。●活动进行时●点击了解↓↓↓●【活动1】727福彩嘉年华&#xff1a;福彩携1000万体验券喊你来生日趴&#xff01;●【活动2】刮刮乐新票“超给力”重磅来袭&#xff01…

进制转换计算机文档,(完整版)计算机各种进制转换练习题(附答案)

进制转换练习题1.十进制数1000对应二进制数为______&#xff0c;对应十六进制数为______。供选择的答案A&#xff1a;①1111101010 ②1111101000 ③1111101100 ④1111101110B&#xff1a;①3C8 ②3D8 ③3E8 ④3F82.十进制小数为0.96875对应的二进制数为______&#xff0c;对应的…

远程计算机 函数不支持,win7远程桌面连接提示要求的函数不受支持怎么办

摘要 腾兴网为您分享:win7远程桌面连接提示要求的函数不受支持怎么办&#xff0c;作业大师&#xff0c;智学网&#xff0c;虚拟助手&#xff0c;喜马拉雅等软件知识&#xff0c;以及winrar纯净&#xff0c;powerdesigner中文&#xff0c;云协同&#xff0c;smc选型软件&#xf…

arcgis vue 添加图层优化_行业 | ArcGIS制图技巧(超全)

↑ 点击上方「中国测绘学会」可快速关注我们地理信息系统(Geographic Information System或 Geo&#xff0d;Information system&#xff0c;GIS)有时又称为“地学信息系统”。它是一种特定的十分重要的空间信息系统。它是在计算机硬、软件系统支持下&#xff0c;对整个或部分地…