Python系列之Python并发执行实现方法

1、Python中并发执行实现方法
1.1 Python中并发执行实现

在Python中,有几种主要的并发执行实现方法,包括多线程、多进程和异步编程。

1.1.1 多线程(Threading)

Python标准库中的threading模块支持多线程编程。然而,由于Python的全局解释器锁(GIL),Python的多线程在CPU密集型任务上并不能实现真正的并行执行。但在I/O密集型任务(如网络请求、文件读写等)上,多线程仍然可以显著提升性能。

import threading  def worker():  print("This is a thread running the worker function.")  # 创建线程对象  
threads = []  
for _ in range(5):  t = threading.Thread(target=worker)  threads.append(t)  t.start()  # 等待所有线程完成  
for t in threads:  t.join()
1.1.2 多进程(Multiprocessing)

Python的multiprocessing模块支持多进程编程,可以充分利用多核CPU的资源。每个进程都有自己独立的Python解释器,因此不受GIL的限制。多进程适用于CPU密集型任务。

import multiprocessing  def worker():  print("This is a process running the worker function.")  if __name__ == "__main__":  processes = []  for _ in range(5):  p = multiprocessing.Process(target=worker)  processes.append(p)  p.start()  # 等待所有进程完成  for p in processes:  p.join()
1.1.3 异步编程(Asyncio)

Python 3.5引入了asyncio模块,支持异步编程。异步编程可以在单线程内实现非阻塞的I/O操作,提高程序的响应速度和吞吐量。它特别适用于处理大量的并发I/O操作,如网络请求。

import asyncio  async def worker():  print("This is an async task running the worker function.")  # 创建事件循环  
loop = asyncio.get_event_loop()  
tasks = []  
for _ in range(5):  task = loop.create_task(worker())  tasks.append(task)  # 执行所有任务  
loop.run_until_complete(asyncio.wait(tasks))  
loop.close()

注意:异步编程与多线程和多进程编程有所不同,它更多的是一种编程模型,而不是简单地创建多个执行单元。异步编程需要理解并适应其特有的编程模式和概念,如协程、事件循环等。

1.2 Python中多进程和多线程的区别

在Python中,multiprocessing和threading模块都用于实现并发执行,但它们在底层机制、使用场景和性能特点上有显著的区别。

1.2.1 Multiprocessing多进程

Multiprocessing模块允许创建多个进程来执行Python代码。每个进程都有自己独立的内存空间和解释器实例,因此它们之间不共享全局变量(除非通过特定的机制,如multiprocessing.Manager或multiprocessing.Value、multiprocessing.Array等)。这使得multiprocessing非常适合于计算密集型任务,因为它可以充分利用多核CPU并行处理任务。

由于每个进程都有自己独立的Python解释器,进程间通信(IPC)通常比线程间通信(通过共享内存)要慢得多,并且需要显式的IPC机制,如管道(Pipe)、队列(Queue)、共享内存(SharedMemory)等。

1.2.2 Threading多线程

Threading模块允许创建多个线程来执行Python代码。线程共享同一个进程的内存空间,因此它们可以直接访问全局变量和大多数Python对象。这使得线程间通信相对简单,因为它们可以直接读写共享的内存。

然而,由于Python的全局解释器锁(GIL)的存在,同一时间内只有一个线程可以执行Python代码。这意味着即使是多线程,CPU密集型任务的执行速度也可能不会显著提高。因此,threading模块在Python中通常更适用于IO密集型任务,如网络请求、文件读写等,这些任务通常可以在一个线程等待IO操作完成时让另一个线程继续执行。

1.2.3 多进程和多线程特性对比
  • 资源共享:
    • 多线程:在多线程中,所有线程共享同一个进程的地址空间,这意味着它们可以访问相同的变量和内存区域。因此,多线程间的数据共享和通信相对简单,但也容易引发数据同步和一致性的问题,如竞态条件。
    • 多进程:每个进程都有自己独立的地址空间,这意味着它们无法直接共享数据。进程间的通信需要通过特殊的机制来实现,如管道、消息队列、共享内存或套接字等。虽然进程间通信相对复杂,但它避免了多线程中的数据同步问题。
  • 全局解释器锁(GIL):
    • 多线程:由于Python的全局解释器锁(GIL)的存在,Python的多线程在CPU密集型任务上并不能实现真正的并行执行。GIL确保任何时候只有一个线程在执行Python字节码。因此,对于计算密集型任务,多线程并不能带来性能提升。
    • 多进程:多进程不受GIL的限制,每个进程都有自己独立的Python解释器,因此可以充分利用多核CPU的资源,实现真正的并行执行。
  • 性能开销:
    • 多线程:线程创建和销毁的开销相对较小,因为线程共享进程的内存空间,无需复制数据。因此,对于需要频繁创建和销毁线程的应用,多线程可能是一个更好的选择。
    • 多进程:进程创建和销毁的开销相对较大,因为每个进程都需要独立的内存空间和系统资源。此外,进程间通信也需要额外的开销。因此,对于需要大量进程的应用,需要谨慎考虑性能问题。
  • 稳定性:
    • 多线程:由于线程共享数据,如果一个线程崩溃,可能会导致整个进程崩溃。
    • 多进程:每个进程都是独立的,一个进程的崩溃不会影响其他进程。因此,多进程在稳定性方面可能更有优势。
  • 适用场景:
    • 多线程:适用于I/O密集型任务,如网络请求、文件读写等。在这些场景下,线程大部分时间都在等待I/O操作完成,因此可以充分利用多线程的优势。
    • 多进程:适用于CPU密集型任务,如科学计算、图像处理等。在这些场景下,多进程可以充分利用多核CPU的资源,实现性能提升。

总之,选择使用多线程还是多进程取决于具体的任务类型和性能需求。在Python中,对于I/O密集型任务,可以使用多线程或异步编程;对于CPU密集型任务,多进程可能是一个更好的选择。

1.3 等待信号量实现并发控制

信号量是一个计数器,用于控制同时访问某个特定资源或资源池的线程数量。信号量有一个值,表示可用的许可数。当线程想要访问资源时,它必须先获取一个许可;如果许可数大于0,则获取成功并减1;否则,线程将阻塞等待。

import threading  sem = threading.Semaphore(3)  # 允许三个线程同时访问资源  def worker():  sem.acquire()  # 获取许可  try:  # 访问或修改共享资源  print("Thread is working with the shared resource.")  finally:  sem.release()  # 释放许可  # 创建并启动线程...
1.3.1 基于等待信号量实现多进程并发

在Python中,基于等待信号量(Semaphore)实现多进程并发通常涉及到multiprocessing模块中的Semaphore类。信号量用于控制对共享资源的访问,允许一定数量的进程同时访问该资源。当信号量的值大于0时,进程可以获得一个信号量许可来访问资源;当信号量的值为0时,进程将阻塞,直到有其他进程释放一个许可。

下面是一个简单的例子,展示了如何使用multiprocessing.Semaphore来实现多进程并发访问共享资源:

import multiprocessing  
import time  
import random  # 设置信号量的初始值,这里允许3个进程同时访问共享资源  
semaphore = multiprocessing.Semaphore(3)  def worker_process(process_id, semaphore):  # 尝试获取信号量许可  semaphore.acquire()  try:  print(f"Process {process_id} acquired semaphore and is working.")  # 模拟工作负载  time.sleep(random.random())  print(f"Process {process_id} finished working and releasing semaphore.")  finally:  # 无论是否发生异常,都要确保释放信号量许可  semaphore.release()  if __name__ == '__main__':  # 创建进程池  processes = []  for i in range(10):  # 创建10个进程  p = multiprocessing.Process(target=worker_process, args=(i, semaphore))  processes.append(p)  p.start()  # 等待所有进程完成  for p in processes:  p.join()  print("All processes have finished.")

在这个例子中创建了10个进程,但是通过信号量限制了同时访问共享资源的进程数最多为3个。每个进程在工作前都会尝试获取信号量的许可,如果信号量的值大于0,则获取许可并开始工作;如果信号量的值为0,则进程会阻塞等待,直到有其他进程释放许可。每个进程完成工作后会释放其持有的信号量许可,这样其他等待的进程就可以获取许可并开始工作。

1.3.2 基于等待信号量实现多线程并发

在Python中,要实现基于等待信号量的多线程并发,可以使用threading模块中的Semaphore类。信号量用于控制对共享资源的并发访问。当信号量的值大于0时,线程可以获得一个信号量许可来访问资源;当信号量的值为0时,线程将阻塞,直到其他线程释放一个许可。

下面是一个简单的例子,展示了如何使用threading.Semaphore来实现多线程并发访问共享资源:

import threading  
import time  
import random  # 设置信号量的初始值,这里允许3个线程同时访问共享资源  
semaphore = threading.Semaphore(3)  def worker_thread(thread_id, semaphore):  # 尝试获取信号量许可  semaphore.acquire()  try:  print(f"Thread {thread_id} acquired semaphore and is working.")  # 模拟工作负载  time.sleep(random.random())  print(f"Thread {thread_id} finished working and releasing semaphore.")  finally:  # 无论是否发生异常,都要确保释放信号量许可  semaphore.release()  if __name__ == '__main__':  # 创建线程列表  threads = []  for i in range(10):  # 创建10个线程  t = threading.Thread(target=worker_thread, args=(i, semaphore))  threads.append(t)  t.start()  # 等待所有线程完成  for t in threads:  t.join()  print("All threads have finished.")

在这个例子中创建了10个线程,但是通过信号量限制了同时访问共享资源的线程数最多为3个。每个线程在工作前都会尝试获取信号量的许可,如果信号量的值大于0,则获取许可并开始工作;如果信号量的值为0,则线程会阻塞等待,直到有其他线程释放许可。每个线程完成工作后会释放其持有的信号量许可,这样其他等待的线程就可以获取许可并开始工作。

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

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

相关文章

MySQL中group_concat()用法

函数用法见链接处 https://www.cnblogs.com/mcj123/articles/17189384.html 使用过程问题:group_concat()拼接后的字符串长度默认限制为1024位字节,超长会被自动截取。 解决: 修改group_concat限制长度。 1.数据库直接通过sql修改 查询限制长…

Dockerfile Docker Compose(实战总结)

Dockerfile & Docker Compose(实战总结) Dockerfile Dockerfile 是用来构建Docker镜像文件,是由一条条构建镜像所需的指令构成的脚步。 步骤: 编写Dockerfile 文件docker build 构建镜像docker run 运行镜像docker push 发…

python coding with ChatGPT 打卡第23天| 回溯算法:理论基础

文章目录 视频讲解回溯法的效率解决的问题如何理解回溯法回溯框架 视频讲解 回溯算法理论篇 回溯是递归的副产品,只要有递归就会有回溯。 回溯法的效率 回溯的本质是穷举,穷举所有可能,然后选出我们想要的答案,如果想让回溯法…

区域规划(Regional Planning)的学习笔记

目录 一、概念题 1.区域的概念、类型、特性 2.区域分析的概念、主要内容 3.自然环境、自然资源的概念 4.区域自然资源评价的内容 5.可持续发展理论定义 6.经济增长、经济结构定义 7.产业结构概念 8.人口增长分析的含义、指标 9.技术进步概念、类型 10.技术进步对区域…

【C++ leetcode】双指针问题(续)

3. 202 .快乐数 题目 编写一个算法来判断一个数 n 是不是快乐数。 「快乐数」 定义为: 对于一个正整数,每一次将该数替换为它每个位置上的数字的平方和。然后重复这个过程直到这个数变为 1,也可能是 无限循环 但始终变不到 1。如果这个过程 结…

Python中的列表推导式(List Comprehension)

Python中的列表推导式(List Comprehension)是一种强大且简洁的语法结构,用于快速创建列表。它通过一行代码就能完成原本需要多行代码才能实现的循环迭代与列表添加操作。列表推导式在Python中非常常用,它使得代码更加简洁、易读和…

wayland(xdg_wm_base) + egl + opengles 光照模型实例(十五)

文章目录 前言一、获取 glm 库文件二、使用环境(Ambient)、漫反射(Diffuse)和镜面(Specular)光照效果的3d 立方体1. egl_wayland_light.cpp2. Matrix.h 和 Matrix.cpp3. xdg-shell-client-protocol.h 和 xdg-shell-protocol.c4. 编译5. 运行总结参考资料前言 本文主要介绍如何…

蓝桥杯2023省赛:阶乘求和

题目链接: 1.阶乘求和 - 蓝桥云课 (lanqiao.cn) 说明: 说难也不难,说简单也不简单,就是考你敏不敏锐。想到末尾位数为0加起来就不影响了,以及阶乘里产生一个10就多一个0就很容易了。 还有蓝桥杯常考的:…

ArcGIS Pro、R和INVEST:三位一体的生态系统服务评估框架

生态系统服务是指生态系统所形成的用于维持人类赖以生存和发展的自然环境条件与效用,是人类直接或间接从生态系统中得到的各种惠益。联合国千年生态系统评估(Millennium ecosystem assessment,MA)提出生态系统服务包括供给、调节、…

flutter->Scaffold左侧/右侧侧边栏

//appBar的 leading/actions 和 Scaffold的drawer/endDrawer 冲突只能存在一个 import package:flutter/material.dart;void main() {runApp(MyApp()); }class MyApp extends StatelessWidget {const MyApp({super.key});overrideWidget build(BuildContext context) {ret…

Bert模型输出:last_hidden_state转换为pooler_output

1. BERT模型的输出 在BERT模型中,last_hidden_state和pooler_output是两个不同的输出。 (1) last_hidden_state: last_hidden_state是指BERT模型中最后一个隐藏层的隐藏状态。它是一个三维张量,其形状为[batch_size, sequence_length, hidden_size]。其…

智慧水务:雨季山区水域水务智能化监控与监测管理方案

一、方案背景 雨季的水务管理对于各区县来说,无疑是一项至关重要的任务。夏季雨水充沛,江河湖泊水位上涨,山洪、上游排水等情况时有发生,给各地的水务设施和防汛工作带来了严峻的挑战。针对区县的各类水域监管场景,需…

免费的chatgpt网站(包含最新版4.0)

相信每个人在生活工作学习中都逃不过用chatgpt来解决一些问题,下面我长话短说,为大家简单介绍几款免费且好用的chatgpt网站 1、YesChat 网址:YesChat-ChatGPT4V Dalle3 Claude 3 All in One Free 第一个就给大家介绍一个狠角色,最…

C 语言中位取反操作符 ~ 和逻辑取反操作符 !

在 C 语言中,有两种取反操作符,分别是位取反操作符 ~ 和逻辑取反操作符 !。 位取反操作符 ~: 位取反操作符 ~ 是一个一元操作符,用于执行按位取反操作。它会将操作数的每个位取反,即将 0 变为 1,将 1 变为 …

c语言入门基础题-自己的解法c小白

编程2:企业发放奖金根据利润提成。利润不高于十万时,奖金可提10%,高于十万时,低于十万部分不变,高于十万部分可提7.5%,20万到40万时,高于20万部分可提5%,40万到六十万时高于四十万部分可提3%&am…

代码随想录算法训练营第五十二天|LeetCode300 最长递增子序列、LeetCode674 最长连续递增序列、LeetCode718 最长重复子数组

300.最长递增子序列 思路:确定dp数组及其下标含义,dp[i]表示以nums[i]为结尾的最长递增子序列长度。递推公式,遍历i以前的子序列, 如果nums[i]>nums[j],dp[i]就等于max(dp[i],dp[j]1),找到当前最长的递增序列长度。…

备战蓝桥杯Day29 - 拼接最大数字问题

问题描述 有n个非负整数,将其按照字符串拼接的方式拼接为一个整数如何拼接可以使得得到的整数最大? 例: 32,94,128,1286,6,71可以拼接除的最大整数为 94716321286128。 问题思路 1.比较两个字符串的第一个数字,数值大的在前面,数值小的在…

算法打卡day21|回溯法篇01|理论知识,Leetcode 77.组合

回溯法理论知识 回溯法也可以叫做回溯搜索法,它是一种搜索的方式。回溯是递归的副产品,只要有递归就会有回溯。所以回溯函数也就是递归函数,指的都是一个函数。 回溯法的效率 回溯法并不是什么高效的算法。因为回溯的本质是穷举,…

win提权第二弹服务提权

阅读须知: 探索者安全团队技术文章仅供参考,未经授权请勿利用文章中的技术资料对任何计算机系统进行入侵操作,由于传播、利用本公众号所提供的技术和信息而造成的任何直接或者间接的后果及损失,均由使用者 本人负责,作者不为此承担任何责任,如…

python多进程调用class类遇到的坑

测试环境 Python 3.9.17 系统:macOS Ventura 13.0.1 测试1 多进程采用test.run方式调用函数。 发现,调用的test.run 函数没运行 def multiprocessing_get_slow_log():pool Pool(3)sp Spider()conn sp.mysql_conncur conn.cursor()sql "&qu…