Python并发编程:多线程与多进程实践

news/2025/3/15 5:40:49/

Python并发编程:多线程与多进程实践

  • 一、并发编程基础概念
    • 1.1 什么是并发编程
    • 1.2 为什么需要并发编程
    • 1.3 并发编程的挑战
  • 二、Python并发编程介绍
    • 2.1 Python的多线程模块
    • 2.2 Python的多进程模块
    • 2.3 协程与异步IO编程
  • 三、Python多线程编程实践
    • 3.1 线程的创建
    • 3.2 线程的生命周期
    • 3.3 线程同步与互斥
      • 3.3.1 Lock
      • 3.3.2 RLock
    • 3.4 线程通信
      • 3.4.1 队列
    • 3.5 线程池与队列
  • 四、Python协程与异步IO编程实践接
    • 1 协程的概念与实现
      • 1.1 协程的概念
      • 1.2 协程的实现
    • 2 使用asyncio进行异步编程
      • 2.1 基本概念与用法
      • 2.2 协程的并发执行
    • 3 实现异步IO操作

一、并发编程基础概念

1.1 什么是并发编程

并发编程是指计算机系统中同时执行多个独立的计算任务。这些任务可以同时执行也可以按照一定的调度策略交替执行。在并发编程中执行的任务需要独立管理自身的资源,且需要与其他任务共享资源。

1.2 为什么需要并发编程

在现代计算机系统中单个CPU的处理速度已经很快了。然而现代应用程序通常需要处理大量的任务和数据,单个CPU无法满足这些需求。因此需要使用多个线程或进程并发处理这些任务和数据,以提高应用程序的执行效率和响应速度。

1.3 并发编程的挑战

虽然并发编程可以提高程序执行效率,但同时也带来了一些挑战:

  • 数据竞争:如果多个线程或进程同时访问共享数据,可能导致数据竞争,从而产生错误和不可预知的结果。
  • 死锁:如果多个线程或进程在等待其他线程或进程释放锁时互相等待,将导致死锁。
  • 上下文切换:线程或进程之间的切换需要涉及上下文切换,这会导致一定的系统开销。

二、Python并发编程介绍

2.1 Python的多线程模块

Python提供了threading模块用于创建和管理多个线程。以下是一个简单的例子:

import threadingdef worker():"""Thread worker function"""print('This is a thread')# Create threads
thread1 = threading.Thread(target=worker)
thread2 = threading.Thread(target=worker)# Start threads
thread1.start()
thread2.start()# Wait for threads to finish
thread1.join()
thread2.join()

上述代码创建了两个线程分别执行worker()函数。使用start()函数启动线程,使用join()函数等待线程执行完毕。

2.2 Python的多进程模块

Python提供了multiprocessing模块可以在多个进程中并发运行Python代码。以下是一个简单的例子:

import multiprocessingdef worker():"""Process worker function"""print('This is a process')# Create processes
process1 = multiprocessing.Process(target=worker)
process2 = multiprocessing.Process(target=worker)# Start processes
process1.start()
process2.start()# Wait for processes to finish
process1.join()
process2.join()

上述代码创建了两个进程分别执行worker()函数。使用start()函数启动进程,使用join()函数等待进程执行完毕。

2.3 协程与异步IO编程

Python提供了asyncio库可以通过协程和异步IO编程实现高效的并发处理。以下是一个简单的例子:

import asyncioasync def worker():"""Asyncio worker function"""print('This is a coroutine')# Create event loop
loop = asyncio.get_event_loop()# Create tasks
task1 = loop.create_task(worker())
task2 = loop.create_task(worker())# Wait for tasks to finish
loop.run_until_complete(asyncio.gather(task1, task2))

上述代码创建了两个协程使用create_task()函数将其转换为任务。使用run_until_complete()函数等待任务执行完毕。

三、Python多线程编程实践

在现代计算机系统中单个CPU的处理速度已经很快了。然而现代应用程序通常需要处理大量的任务和数据,单个CPU无法满足这些需求。因此使用多个线程并发处理这些任务和数据,以提高应用程序的执行效率和响应速度。接下来将介绍Python中的多线程编程实践。

3.1 线程的创建

Python中创建线程非常简单只需使用threading模块即可。以下是一个简单的例子:

import threading# 定义线程执行的函数
def worker():print('This is a thread')# 创建线程
thread = threading.Thread(target=worker)# 启动线程
thread.start()# 等待线程执行完毕
thread.join()

上述代码创建了一个线程使用start()函数启动它,使用join()函数等待线程执行完毕。

3.2 线程的生命周期

线程的生命周期包括以下几个阶段:

  • 创建阶段:线程被创建,但尚未执行。
  • 就绪阶段:线程已经准备好执行,等待CPU资源。
  • 执行阶段:线程正在执行其任务。
  • 阻塞阶段:线程暂时被挂起,等待某个事件的发生。
  • 终止阶段:线程执行完毕,或由于异常终止。

3.3 线程同步与互斥

在多线程编程中可能会涉及共享资源的访问问题,如果多个线程同时访问同一个共享资源,会导致数据竞争和错误的结果。因此需要使用线程同步和互斥机制。

3.3.1 Lock

Lock是一种简单的互斥机制用于保护共享资源的访问。以下是一个简单的例子:

import threading# 定义共享变量
count = 0# 定义锁
lock = threading.Lock()# 定义计数函数
def counter():global countfor i in range(1000000):lock.acquire()count += 1lock.release()# 创建线程
thread1 = threading.Thread(target=counter)
thread2 = threading.Thread(target=counter)# 启动线程
thread1.start()
thread2.start()# 等待线程执行完毕
thread1.join()
thread2.join()# 输出计数结果
print(count)

上述代码使用Lock保护共享变量的访问,确保每个线程都可以正确地访问和修改共享变量。

3.3.2 RLock

RLock是一种可重入的互斥机制,允许同一个线程对同一个锁进行多次加锁和解锁。以下是一个简单的例子:

import threading# 定义共享变量
count = 0# 定义锁
lock = threading.RLock()# 定义计数函数
def counter():global countfor i in range(1000000):lock.acquire()lock.acquire()  # 多次加锁count += 1lock.release()lock.release()  # 多次解锁# 创建线程
thread1 = threading.Thread(target=counter)
thread2 = threading.Thread(target=counter)# 启动线程
thread1.start()
thread2.start()# 等待线程执行完毕
thread1.join()
thread2.join()# 输出计数结果
print(count)

上述代码使用RLock保护共享变量的访问,允许同一个线程对同一个锁进行多次加锁和解锁。

3.4 线程通信

在多线程编程中可能需要线程之间进行通信,以共享数据或进行协作。Python提供了多种方式来实现线程之间的通信。

3.4.1 队列

队列是一种线程安全的数据结构可以用来进行线程之间的通信。以下是一个简单的例子:

import threading
import queue# 定义队列
q = queue.Queue()# 定义生产者函数
def producer():for i in range(10):q.put(i)print('生产者生产了', i)# 定义消费者函数
def consumer():while True:item = q.get()if item is None:breakprint('消费者消费了', item)# 创建线程
thread1 = threading.Thread(target=producer)
thread2 = threading.Thread(target=consumer)# 启动线程
thread1.start()
thread2.start()# 等待生产者线程执行完毕
thread1.join()# 将None添加到队列中,通知消费者线程结束
q.put(None)# 等待消费者线程执行完毕
thread2.join()

上述代码使用队列实现了生产者-消费者模式,实现了线程之间的通信。

3.5 线程池与队列

线程池是一种常见的优化多线程程序的方式,可以有效地复用线程,避免频繁地创建和销毁线程。Python提供了concurrent.futures模块,可以很容易地创建和管理线程池。以下是一个简单的例子:

import concurrent.futures
import time# 定义任务函数
def worker(i):print('Thread', i, 'start')time.sleep(1)print('Thread', i, 'end')# 创建线程池
with concurrent.futures.ThreadPoolExecutor(max_workers=3) as executor:# 提交任务futures = [executor.submit(worker, i) for i in range(10)]# 等待任务完成for future in concurrent.futures.as_completed(futures):result = future.result()

上述代码创建了一个包含3个线程的线程池,提交了10个任务。使用as_completed()函数等待任务执行完毕。

除了常规的线程池外,Python还提供了使用队列实现的线程池,可以更好地控制任务的执行方式。以下是一个简单的例子:

import concurrent.futures
import time
import queue# 定义任务函数
def worker(i, q):print('Thread', i, 'start')item = q.get()time.sleep(1)print('Thread', i, 'finish', item)q.task_done()# 定义任务队列
q = queue.Queue()# 向队列中添加任务
for i in range(10):q.put(i)# 创建线程池
with concurrent.futures.ThreadPoolExecutor(max_workers=3) as executor:# 提交任务futures = [executor.submit(worker, i, q) for i in range(3)]# 等待任务完成q.join()# 取消未执行的任务for future in futures:future.cancel()

上述代码使用队列实现了线程池,对于每个任务,只有一个线程可以执行,避免了竞争的情况。使用队列的join()函数等待所有任务完成。未执行的任务可以使用cancel()函数取消。

四、Python协程与异步IO编程实践接

下面将介绍Python中的协程与异步IO编程实践

1 协程的概念与实现

1.1 协程的概念

协程是一种轻量级的线程可以在单个线程内部实现并发执行。协程能够在不同的执行点之间暂停和继续执行,类似于线程的上下文切换。协程的优点在于避免了线程的创建和上下文切换开销,同时也避免了多线程程序中可能存在的数据竞争和锁问题。

1.2 协程的实现

Python的协程通过生成器实现使用yield语句来暂停和恢复协程的执行。以下是一个简单的例子:

# 定义协程函数
def coroutine():print('Coroutine start')while True:value = yieldprint('Coroutine received:', value)# 创建协程对象
coro = coroutine()# 启动协程
next(coro)# 向协程发送数据
coro.send('data1')
coro.send('data2')

上述代码创建了一个协程对象,使用yield语句来暂停和恢复协程的执行。使用next()函数启动协程,使用send()函数向协程发送数据。

2 使用asyncio进行异步编程

Python提供了asyncio模块可以很容易地进行协程和异步IO编程。asyncio提供了一个事件循环(Event Loop),可以在单线程中实现异步的协程调度和IO操作。

2.1 基本概念与用法

以下是一个简单的例子展示了如何创建和启动一个协程,以及如何使用事件循环:

import asyncio# 定义协程函数
async def coroutine():print('Coroutine start')await asyncio.sleep(1)print('Coroutine end')# 创建事件循环
loop = asyncio.get_event_loop()# 启动协程
loop.run_until_complete(coroutine())# 关闭事件循环
loop.close()

上述代码中使用async关键字定义了一个协程函数,使用await关键字来暂停和恢复协程的执行。使用asyncio.get_event_loop()函数创建事件循环,使用loop.run_until_complete()函数启动协程,并等待协程执行结束。最后使用loop.close()函数关闭事件循环。

2.2 协程的并发执行

asyncio可以很容易地实现多个协程的并发执行。以下是一个简单的例子:

import asyncio# 定义协程函数
async def coroutine(i):print('Coroutine', i, 'start')await asyncio.sleep(1)print('Coroutine', i, 'end')# 创建事件循环
loop = asyncio.get_event_loop()# 启动多个协程
coros = [coroutine(i) for i in range(5)]
loop.run_until_complete(asyncio.gather(*coros))# 关闭事件循环
loop.close()

上述代码创建了多个协程对象使用asyncio.gather()函数启动这些协程,并等待它们执行完毕。使用*coros语法将协程列表解包为单独的参数。

3 实现异步IO操作

除了协程的并发执行外,asyncio还提供了异步IO操作的实现,以提高应用程序的执行效率和响应速度。以下是一个简单的例子,展示了如何使用异步IO进行文件的读取和写入:

import asyncio# 定义异步IO操作函数
async def file_io():# 打开文件with open('test.txt', 'r') as f1, open('output.txt', 'w') as f2:# 读取文件data = await f1.read()# 写入文件await f2.write(data)# 创建事件循环
loop = asyncio.get_event_loop()# 启动异步IO操作
loop.run_until_complete(file_io())# 关闭事件循环
loop.close()

上述代码使用async with语法打开文件,并使用await关键字进行异步IO操作。在文件读取和写入操作之间使用await asyncio.sleep()函数来模拟IO操作的延迟。

除此之外asyncio还提供了许多其他的异步IO操作,如TCP连接、UDP连接、HTTP请求等可以根据需要进行选择和使用。


http://www.ppmy.cn/news/70408.html

相关文章

11.PC端网页特效

PC端网页特效 1. 元素偏移量 offset 系列 1.1 offset 概述 offset 翻译过来就是偏移量, 使用 offset 系列相关属性可以动态的得到该元素的位置(偏移)、大小等 获得元素距离带有定位父元素的位置获得元素自身的大小(宽度高度&a…

【数据分享】2014-2023年全国监测站点的逐月空气质量数据(15个指标\shp\excel格式)

空气质量的好坏反映了空气的污染程度,在各项涉及城市环境的研究中,空气质量都是一个十分重要的指标。空气质量是依据空气中污染物浓度的高低来判断的。 我们发现学者王晓磊在自己的主页里面分享了2014年5月以来的全国范围的到站点的逐时空气质量数据&am…

【Linux升级之路】3_Linux进程概念

🌟hello,各位读者大大们你们好呀🌟 🍭🍭系列专栏:【Linux升级之路】 ✒️✒️本篇内容:认识冯诺依曼系统,操作系统概念与定位,深入理解进程概念(了解PCB&…

渗透无回显,放弃 or 看这篇文章

渗透无回显,放弃 or 看这篇文章 1.DNSlog工具2.DNSlog回显原理3.无回显的XXE 1.DNSlog工具 如果有自己的服务器和域名,可以自建一个这样的DNSlog平台用于进行渗透数据外带,或者直接使用BugScan团队开源的工具搭建即可: Github:D…

SpringBoot中@ControllerAdvice/@RestControlAdvice+@ExceptionHandler实现全局异常捕获与处理

场景 在编写Controller接口时,为避免接口因为未知的异常导致返回不友好的结果和提示。 如果不进行全局异常捕获则需要对每个接口进行try-catch或其他操作。 可以对Controller进行全局的异常捕获和处理,一旦发生异常,则返回通用的500响应码与…

typescript里面正则的使用

以下是一些常用的正则表达式元字符: ^:匹配字符串的开头。$:匹配字符串的结尾。.:匹配任意单个字符,除了换行符。*:匹配前面的字符零次或多次。:匹配前面的字符一次或多次。?:匹配…

无效数据处理之道:Linux系统编程C/C++实践探索

无效数据处理之道:Linux系统编程C/C实践探索 一、引言1.1 无效数据的来源1. 用户输入2. 硬件故障3. 软件错误 1.2 检测和处理无效数据1. 数据验证2. 异常处理3. 断言4. 内存管理 1.3 无效数据的预防和处理最佳实践1. 设计健壮的输入验证机制2. 错误处理和恢复机制3.…

Vue3(6) Transition

目录 组件 基于CSS的过渡效果 JavaScript钩子 Vue 提供了两个内置组件&#xff0c;可以帮助你制作基于状态变化的过渡和动画&#xff1a; <Transition> 会在一个元素或组件进入和离开 DOM 时应用动画。 <TransitionGroup> 会在一个 v-for 列表中的元素或组件被…