Python并发编程(多线程与多进程实践)

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

一、并发编程

1.1 什么是并发编程

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

1.2 为什么需要并发编程

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

1.3 并发编程的挑战

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

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

二、Python并发编程介绍

2.1 Python的多线程模块

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

import threading

def 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 multiprocessing

def 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 asyncio

async 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 count
    for i in range(1000000):
        lock.acquire()
        count += 1
        lock.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 count
    for i in range(1000000):
        lock.acquire()
        lock.acquire()  # 多次加锁
        count += 1
        lock.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:
            break
        print('消费者消费了', 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 = yield
        print('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请求等可以根据需要进行选择和使用。

目录
相关文章
|
10天前
|
并行计算 数据处理 调度
Python中的并发编程:探索多线程与多进程的奥秘####
本文深入探讨了Python中并发编程的两种主要方式——多线程与多进程,通过对比分析它们的工作原理、适用场景及性能差异,揭示了在不同应用需求下如何合理选择并发模型。文章首先简述了并发编程的基本概念,随后详细阐述了Python中多线程与多进程的实现机制,包括GIL(全局解释器锁)对多线程的影响以及多进程的独立内存空间特性。最后,通过实例演示了如何在Python项目中有效利用多线程和多进程提升程序性能。 ####
|
12天前
|
存储 安全 Java
Java多线程编程的艺术:从基础到实践####
本文深入探讨了Java多线程编程的核心概念、应用场景及其实现方式,旨在帮助开发者理解并掌握多线程编程的基本技能。文章首先概述了多线程的重要性和常见挑战,随后详细介绍了Java中创建和管理线程的两种主要方式:继承Thread类与实现Runnable接口。通过实例代码,本文展示了如何正确启动、运行及同步线程,以及如何处理线程间的通信与协作问题。最后,文章总结了多线程编程的最佳实践,为读者在实际项目中应用多线程技术提供了宝贵的参考。 ####
|
9天前
|
监控 安全 Java
Java中的多线程编程:从入门到实践####
本文将深入浅出地探讨Java多线程编程的核心概念、应用场景及实践技巧。不同于传统的摘要形式,本文将以一个简短的代码示例作为开篇,直接展示多线程的魅力,随后再详细解析其背后的原理与实现方式,旨在帮助读者快速理解并掌握Java多线程编程的基本技能。 ```java // 简单的多线程示例:创建两个线程,分别打印不同的消息 public class SimpleMultithreading { public static void main(String[] args) { Thread thread1 = new Thread(() -> System.out.prin
|
12天前
|
Java UED
Java中的多线程编程基础与实践
【10月更文挑战第35天】在Java的世界中,多线程是提升应用性能和响应性的利器。本文将深入浅出地介绍如何在Java中创建和管理线程,以及如何利用同步机制确保数据一致性。我们将从简单的“Hello, World!”线程示例出发,逐步探索线程池的高效使用,并讨论常见的多线程问题。无论你是Java新手还是希望深化理解,这篇文章都将为你打开多线程的大门。
|
22天前
|
Java Unix 调度
python多线程!
本文介绍了线程的基本概念、多线程技术、线程的创建与管理、线程间的通信与同步机制,以及线程池和队列模块的使用。文章详细讲解了如何使用 `_thread` 和 `threading` 模块创建和管理线程,介绍了线程锁 `Lock` 的作用和使用方法,解决了多线程环境下的数据共享问题。此外,还介绍了 `Timer` 定时器和 `ThreadPoolExecutor` 线程池的使用,最后通过一个具体的案例展示了如何使用多线程爬取电影票房数据。文章还对比了进程和线程的优缺点,并讨论了计算密集型和IO密集型任务的适用场景。
43 4
|
22天前
|
缓存 Java 调度
Java中的多线程编程:从基础到实践
【10月更文挑战第24天】 本文旨在为读者提供一个关于Java多线程编程的全面指南。我们将从多线程的基本概念开始,逐步深入到Java中实现多线程的方法,包括继承Thread类、实现Runnable接口以及使用Executor框架。此外,我们还将探讨多线程编程中的常见问题和最佳实践,帮助读者在实际项目中更好地应用多线程技术。
24 3
|
22天前
|
调度 iOS开发 MacOS
python多进程一文够了!!!
本文介绍了高效编程中的多任务原理及其在Python中的实现。主要内容包括多任务的概念、单核和多核CPU的多任务实现、并发与并行的区别、多任务的实现方式(多进程、多线程、协程等)。详细讲解了进程的概念、使用方法、全局变量在多个子进程中的共享问题、启动大量子进程的方法、进程间通信(队列、字典、列表共享)、生产者消费者模型的实现,以及一个实际案例——抓取斗图网站的图片。通过这些内容,读者可以深入理解多任务编程的原理和实践技巧。
46 1
|
4天前
|
数据采集 Java Python
爬取小说资源的Python实践:从单线程到多线程的效率飞跃
本文介绍了一种使用Python从笔趣阁网站爬取小说内容的方法,并通过引入多线程技术大幅提高了下载效率。文章首先概述了环境准备,包括所需安装的库,然后详细描述了爬虫程序的设计与实现过程,包括发送HTTP请求、解析HTML文档、提取章节链接及多线程下载等步骤。最后,强调了性能优化的重要性,并提醒读者遵守相关法律法规。
28 0
|
4月前
|
运维 关系型数据库 MySQL
掌握taskset:优化你的Linux进程,提升系统性能
在多核处理器成为现代计算标准的今天,运维人员和性能调优人员面临着如何有效利用这些处理能力的挑战。优化进程运行的位置不仅可以提高性能,还能更好地管理和分配系统资源。 其中,taskset命令是一个强大的工具,它允许管理员将进程绑定到特定的CPU核心,减少上下文切换的开销,从而提升整体效率。
掌握taskset:优化你的Linux进程,提升系统性能
|
4月前
|
弹性计算 Linux 区块链
Linux系统CPU异常占用(minerd 、tplink等挖矿进程)
Linux系统CPU异常占用(minerd 、tplink等挖矿进程)
171 4
Linux系统CPU异常占用(minerd 、tplink等挖矿进程)
下一篇
无影云桌面