当前位置: 首页 > news >正文

并发编程-线程

并发编程-线程

  • 一个进程是操作系统中运行的一个任务,进程独立拥有CPU、内存等资源
  • 一个线程是一个进程中运行的一个任务,线程之间共享CPU、内存等资源,进程里的每一个任务都是线程。

线程创建

创建线程:使用threading模块中的Thread类来创建线程

  • target表示线程执行的任务
  • args表示任务的参数,是一个元组
  • start()方法指启动线程
  • join()方法指等待线程结束
from threading import Threaddef task(count:int):...thread1 = Thread(target=task, args=(10,))  # 创建子线程thread1
thread1.start()  # 启动子线程

启动两个线程执行task任务

from threading import Threaddef task(count: int):for i in range(count):print(i)thread1 = Thread(target=task, args=(10,))
thread2 = Thread(target=task, args=(20,))thread1.start()  # 启动线程1
thread2.start()	 # 启动线程2
print("main thread is end")  # 执行到此处,主线程无任务,自行结束

输出结果:

0
1
2
3
4
5
06main thread is end78
9
1
2
3
4
5
6
7
8
9

等待主线程结束:

from threading import Threaddef task():for i in range(10):print(i)thread1 = Thread(target=task)
thread2 = Thread(target=task)thread1.start()
thread2.start()
thread1.join()  # 等待线程执行结束
thread2.join()  # 等待线程执行结束
print("main thread is end")  # 等到线程1和线程2任务执行完毕,主线程才会结束

输出结果:

0
1
2
3
4
50
6
7
8
9
12
3
4
5
6
7
8
9
main thread is end

通过继承创建线程

通过继承Thread类来创建线程

打印混乱的原因:多线程执行时,print函数打印内容后会再打印换行,导致换行来不及打印,出现打印混乱

解决办法:行尾手动加换行,print函数end参数设置为空字符串

print(f"{self.name} - {i}\n", end="")
from threading import Threadclass MyThread(Thread):def run(self) -> None:# 线程的任务脚本pass
import time
from threading import Threadclass MyThread(Thread):def __init__(self, name: str, count: int):super().__init__()# 该方法已弃用,使用name属性替换# self.setName()self.name = name  # 设置线程名称self.count = countdef run(self) -> None:# 线程的任务脚本for i in range(self.count):print(f"{self.name} - {i}\n", end="")# 休眠10毫秒time.sleep(0.01)t1 = MyThread("a", 10)
t2 = MyThread("b", 10)
t1.start()
t2.start()

输出结果:

D:\Python3.11\python.exe F:/Code/python_code/high_python/python_advance/thread/demo01_class.py
a - 0
b - 0
a - 1
b - 1
b - 2
a - 2
a - 3
b - 3
b - 4
a - 4
b - 5
a - 5
b - 6
a - 6
b - 7
a - 7
b - 8
a - 8
b - 9
a - 9

守护线程

主线程结束,守护线程会自动结束,这就叫守护线程

  • 守护线程会在主线程结束时候自动结束
  • 主线程则需要等待到所有的非守护线程结束才能结束
  • 守护线程一般用于非关键性的线程,比如:日志
from threading import Threaddef task():for i in range(10):print(i)thread = Thread(target=task, daemon=True)
thread.start()

输出结果:

0
Process finished with exit code 0

等待线程结束

from threading import Threaddef task():for i in range(10):print(i)thread = Thread(target=task, daemon=True)
thread.start()
thread.join()

输出结果:

D:\Python3.11\python.exe F:/Code/python_code/high_python/python_advance/thread/demo03_daemon_thread.py
0
1
2
3
4
5
6
7
8
9Process finished with exit code 0

继承类设置守护线程

class MyThread(Thread):def __init__(self, name: str, count: int):super().__init__()# 该方法已弃用,使用name属性替换# self.setName()self.name = name  # 设置线程名称self.count = countself.daemon = Truedef run(self) -> None:# 线程的任务脚本for i in range(self.count):print(f"{self.name} - {i}\n", end="")# 休眠10毫秒time.sleep(0.01)t1 = MyThread("a", 10)
t2 = MyThread("b", 10)
t1.start()
t2.start()
t1.join() # 等待t1结束,结束主线程

线程安全队列

queue模块中的Queue类提供了线程安全队列功能

  • put(item, block=False) # 队列元素满时,不阻塞,会抛出异常,为True时,会让线程阻塞等待, 可能会导致线程卡死
  • put(item, timeout=3) # 队列元素满时,等待timeout时间,如果超出时间则抛出异常
  • get(block=False) # 从队列取元素,如果队列为空则不阻塞,会抛出异常
  • get(timeout=10) # 从队列取元素,如果队列为空则则赛等待10秒,超时抛出异常
  • qsize() # 队列大小
  • empty() # 判断队列是否为空
  • full() # 判断队列是否满的

生产者消费者线程实例

from threading import Thread
from queue import Queueclass Producer(Thread):"""生产者"""def __init__(self, name: str, count: int, queue: Queue) -> None:super(Producer, self).__init__()# 线程自带属性self.name = name# 自定义属性self.count = countself.queue = queuedef run(self) -> None:for n in range(self.count):msg = f"{self.name} - {n}"self.queue.put(msg, block=True)class Consumer(Thread):"""消费者"""def __init__(self, name: str, queue: Queue) -> None:super().__init__()self.name = nameself.daemon = Trueself.queue = queuedef run(self) -> None:while True:msg = self.queue.get(block=True)print(f"{self.name} - {msg}\n", end="")queue = Queue(maxsize=3)
threads = [Producer("p1", 10, queue),Producer("p2", 10, queue),Producer("p3", 10, queue),Consumer("c1", queue),Consumer("c2", queue),
]for t in threads:t.start()

线程池

  • 线程的创建和销毁相对比较耗费资源
  • 频繁的创建和销毁线程不利于高性能
  • 线程池是python提供的便于线程管理和提高性能的工具

python提供两个类来管理线程

  1. concurrent.futures.ThreadPoolExecutor
    • submit() # 启动/执行一个任务,返回结果是一个Future对象
    • map() # 多个任务执行,将不同参数分配到每一个任务中
    • shutdown() # 关闭线程池
  2. Future
    • result() # 任务执行结果
    • exception() # 任务异常信息

方式一:适用于不同任务

from concurrent.futures import ThreadPoolExecutor
import timedef task(name: str):print(f"{name} - step 1\n", end="")time.sleep(1)print(f"{name} - step 2\n", end="")return f"{name} complete"with ThreadPoolExecutor() as executor:result_1 = executor.submit(task, "A")result_2 = executor.submit(task, "B")print(result_1.result())  # result()会等待有结果再返回print(result_2.result())"""
A - step 1
B - step 1
A - step 2
A complete
B - step 2
B complete
"""

方式二:map()适用于同一任务,不同参数

from concurrent.futures import ThreadPoolExecutor
import timedef task(name: str) -> str:print(f"{name} - step 1\n", end="")time.sleep(1)print(f"{name} - step 2\n", end="")return f"{name} complete"with ThreadPoolExecutor() as executor:results = executor.map(task, ["C", "D"])for r in results:print(r)"""
C - step 1
D - step 1
D - step 2
C - step 2
C complete
D complete
"""

多线程案例:下载图片

from concurrent.futures import ThreadPoolExecutor
from urllib.request import urlopen, Request
import osdef download_img(url: str):site_url = Request(url, headers={})with urlopen(site_url) as web_file:img_data = web_file.read()if not img_data:raise Exception(f"Error: can not load the image from {url}")file_name = os.path.basename(url)with open(file_name, "wb") as f:f.write(img_data)return "Download image successfully, {}".format(url)urls = ["https://img0.bdstatic.com/static/searchresult/img/baseimg3_4f26a23.png",# "..."
]
headers = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36","content-type": "Content-Type: image/jpeg"
}
with ThreadPoolExecutor() as ececutor:results = ececutor.map(download_img, urls)for r in results:print(r)

执行结果:

PS F:\Code\python_code\high_python\python_advance\thread> python .\thread_pool_demo.py
Download image successfully, https://img0.bdstatic.com/static/searchresult/img/baseimg3_4f26a23.png
http://www.lryc.cn/news/26863.html

相关文章:

  • 图解LeetCode——剑指 Offer 34. 二叉树中和为某一值的路径
  • 使用Python免费试用最新Openai API
  • 04、启动 SVN 服务器端程序
  • jsp船舶引航计费网站Myeclipse开发mysql数据库web结构java编程计算机网页项目
  • Allegro如何画半圆形的线操作指导
  • 【强烈建议收藏:MySQL面试必问系列之SQL语句执行专题】
  • 详解Linux下的环境变量以及C++库文件和头文件、python库的配置
  • 企业级分布式数据库 - GaussDB介绍
  • Linux I2C 驱动实验
  • DC-DC模块电源隔离直流升压高压稳压输出5v12v24v转60v100v110v150v220v250v300v400v500v
  • EF有几种模式,EF的三种模式分别是什么?
  • 数据可视化展示:打工人常见职业病,颈腰椎病占比最高达66.51%
  • 【食品图像识别】Large Scale Visual Food Recognition
  • RAN-in-the-Cloud:为 5G RAN 提供云经济性
  • vector、list、queue
  • 操作系统面经
  • 一天约了4个面试,复盘一下面试题和薪资福利
  • 详解单链表(内有精美图示哦)
  • csdn文章导航
  • 【Spring】掌握 Spring Validation 数据校验
  • 定语 从句
  • 【数据可视化工具】浅谈 DataEase 和 FineBI 支持的数据源
  • 100种思维模型之上帝视角思维模型-025
  • 从这5个方面,总结我当PM的第一年
  • ChatGPT可以作为一个翻译器吗?
  • 详述java的设计模式(三)
  • Linux命令·pwd
  • 以图搜图服务快速搭建
  • 【TensorFlow安装踩坑记录】
  • 03.03回溯法