Python多线程编程详解

news2025/2/23 19:18:16

    

 


概要

进程(process)指的是正在运行的程序的实例,当我们执行某个程序时,进程就被操作系统创建了。而线程(thread)则包含于进程之中,是操作系统能够进行运算调度的最小单元,多个线程可以同处一个进程中,且同时处理不同的任务。一条进程中可以并发多个线程,而同一条线程将共享该进程中的全部系统资源。

每个进程都有自己独立的地址空间、内存和数据栈,因此进程之间通讯不方便,所以需使用用进程间通讯(InterProcess Communication, IPC)。而同一个进程中的线程共享资源,因此线程间通讯非常方便,只需注意数据同步与互斥的问题。

Python支持多线程编程和多进程编程。在本教程中,我们将学习有关Python多线程编程的基础知识、线程同步、线程池以及如何使用多进程。


1. 多线程基础

线程是进程内的执行单元,每个进程都至少有一个线程。Python标准库提供了thread模块和threading模块来支持线程编程。在Python3中,thread模块已经改名为_thread,并在_thread基础上开发了更为强大的threading模块,因此我们可以使用threading.Thread类创建线程对象,并通过start()方法启动线程。

import threading



def worker():

    print("I am a thread.")



t = threading.Thread(target=worker)

t.start()

在上面的代码中,我们创建了一个名为worker的函数,它是线程的工作内容。我们使用threading.Thread类创建了一个线程对象t,并将worker作为参数传递给了Thread构造函数。然后,我们调用t.start()方法来启动线程。当我们运行这段代码时,会看到以下输出:

I am a thread.

另外,我们也可以通过继承Thread类并重写run方法来实现线程的创建

import threading



class MyThread(threading.Thread):

    def __init__(self, n):

        self.n = n

        super().__init__() # 调用父类函数初始化线程

    def run(self):

        print('线程:', self.n)

for i in range(1,3):

    t = MyThread(i)

    t.start()

结果应该输出打印出:

线程1

线程2

2. 多线程同步

当多个线程同时访问共享资源时,可能会发生竞态条件。为避免这种情况,我们需要使用线程同步机制。Python提供了锁(Lock)、信号量(Semaphore)、事件(Event)等机制来实现线程同步。

下面是一个使用锁来确保线程同步的示例:

import threading



lock = threading.Lock()



count = 0



def worker():

    global count

    lock.acquire()

    try:

        count += 1

    finally:

        lock.release()



threads = []

for i in range(10):

    t = threading.Thread(target=worker)

    threads.append(t)



for t in threads:

    t.start()



for t in threads:

    t.join()



print(count)

在上面的代码中,我们定义了一个全局变量count和一个锁对象lockworker函数是每个线程的工作内容,它通过lock来确保count的修改是原子性的。我们创建了10个线程并启动它们,等待所有线程执行完毕后输出count的值。当我们运行这段代码时,会看到以下输出:

10

如果把锁去掉后,count的值会是几呢?动手试试吧!会有意向不到的结果哦~

3. 线程池

线程池可以用来提高代码的性能,在需要大量线程处理任务时,可以使用线程池来减少线程的创建和销毁次数,提高线程的复用率。

Python标准库提供了concurrent.futures模块来支持线程池编程。我们可以使用ThreadPoolExecutor类来创建和管理线程池。

下面是一个使用线程池来处理任务的示例:

from concurrent.futures import ThreadPoolExecutor



def worker(num):

    return num * num



executor = ThreadPoolExecutor(max_workers=4)



results = []

for i in range(10):

    result = executor.submit(worker, i)

    results.append(result)



for result in results:

    print(result.result())

在上面的代码中,我们定义了一个worker函数用来处理任务。我们创建了一个ThreadPoolExecutor对象executor,并将最大工作线程数设置为4。然后,我们使用submit方法提交10个任务给executor,并将结果保存在results列表中。最后,我们遍历results列表并输出每个任务的结果。

4. 多进程编程

进程是计算机中程序执行的基本单位,而线程则是进程中执行代码的单位。多进程编程就是利用多个独立运行的进程来完成任务。相比单进程,多进程有以下优点:

• 提高了程序的并发性能。

• 可以更好地利用多核CPU

• 能够处理更大的数据集。

在进行多进程编程时,需要注意进程之间的通信和同步问题。

5. Python实现多进程编程

Python标准库中的multiprocessing模块提供了实现多进程编程的工具。其中常用的方法包括:

• Process:创建一个新进程。

• Pool:创建一组进程池。

• Queue:进程之间的消息队列。

下面是一个简单的示例,展示如何使用multiprocessing模块创建子进程:

import multiprocessing



def worker():

    """子进程要执行的任务"""

    print('子进程正在运行')



if __name__ == '__main__':

    p = multiprocessing.Process(target=worker)

    p.start()

    print('主进程已经结束')

在这个例子中,我们创建一个新的进程,并将其target属性指向worker函数。然后使用start方法启动该进程。注意到在Windows系统中,需要将启动进程的代码放在if __name__ == '__main__':语句内,以避免出现一些问题。

5.1 Python进程池

import multiprocessing



def worker(num):

    """子进程要执行的任务"""

    print(f'子进程{num}正在运行')



if __name__ == '__main__':

    with multiprocessing.Pool(processes=4) as pool:

        results = pool.map(worker, range(4))

        print(results)

在这个例子中,我们使用了进程池来管理多个子进程。首先创建一个Pool对象,其中processes参数指定了进程池的大小。然后使用map方法提交任务,该方法会自动分配任务给空闲的子进程并返回结果。最后打印输出结果。

5.2 在进程之间进行通信

import multiprocessing



def writer(q):

    """写入数据"""

    for i in range(10):

        q.put(i)



def reader(q):

    """读取数据"""

    while True:

        item = q.get()

        if item is None:

            break

        print(item)



if __name__ == '__main__':

    q = multiprocessing.Queue()



    # 启动子进程

    p1 = multiprocessing.Process(target=writer, args=(q,))

    p2 = multiprocessing.Process(target=reader, args=(q,))

    p1.start()

    p2.start()



    # 等待子进程结束

    p1.join()

    q.put(None)

    p2.join()

在这个例子中,我们创建了一个消息队列,并通过Queue对象将其传递给两个子进程。一个子进程负责将数据写入队列中,而另一个子进程则从队列中读取数据并输出到屏幕上。注意到在程序末尾我们向队列中添加了None元素,以便通知读取数据的子进程结束循环。

6.多进程计算圆周率

下面是使用Python多进程和蒙特卡罗(Monte Carlo)方法估计圆周率的示例代码:

import random

from multiprocessing import Pool



def estimate_pi(n):

    num_points_inside_circle = 0

    for _ in range(n):

        x = random.uniform(-1, 1)

        y = random.uniform(-1, 1)

        if x**2 + y**2 <= 1:

            num_points_inside_circle += 1

    return 4 * num_points_inside_circle / n



if __name__ == '__main__':

    num_processes = 4

    num_samples = int(1e8)

    with Pool(num_processes) as p:

        results = p.map(estimate_pi, [int(num_samples/num_processes)] * num_processes)

    print(sum(results)/num_processes)

该代码使用random模块中的uniform函数生成在$[-1, 1]$范围内均匀分布的随机数,并统计落入以原点为圆心,半径为1的圆中的点数。最后,将所有子进程的结果相加并除以进程数以得到圆周率的估计值。我算出的结果是3.1416664800000005, 你们呢?

其中,Pool(num_processes) 创建一个具有num_processes个进程的进程池,p.map(estimate_pi, [int(num_samples/num_processes)] * num_processes) 在每个进程上调用estimate_pi函数,并将输入参数设为所需的样本数量的四分之一,然后返回其结果。

7.总结

Python多线程与进程编程中,我们不仅要理解和掌握的关于threading 模块和multiprocessing模块如何使用,更要掌握的是线程和进程之间的区别和联系以及线程通信、进程通信的方式。

欢迎点赞收藏转发,感谢!

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

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

相关文章

【AntDB数据库】AntDB数据库告警管理

告警历史 功能概述 数据库系统的主机、单节点集群的被监测指标达到告警阀值时&#xff0c;AMOPS就会产生告警并展示在告警分类页面上。 告警分类页面提供告警搜索查看功能&#xff0c;用户可以指定监控项、集群、事件级别、时间范围和告警对象对告警进行搜索。 查询的告警数…

Android12之执行adb disable-verity后android无法启动(一百五十六)

简介&#xff1a; CSDN博客专家&#xff0c;专注Android/Linux系统&#xff0c;分享多mic语音方案、音视频、编解码等技术&#xff0c;与大家一起成长&#xff01; 优质专栏&#xff1a;Audio工程师进阶系列【原创干货持续更新中……】&#x1f680; 人生格言&#xff1a; 人生…

AotucCrawler 快速爬取图片

AotucCrawler 快速爬取图片 今天介绍一款自动化爬取图片项目。 GitHub: GitHub - YoongiKim/AutoCrawler: Google, Naver multiprocess image web crawler (Selenium) Google, Naver multiprocess image web crawler (Selenium) 关键字 爬虫网站&#xff1a;Google、Naver &…

【深度学习】2-3 神经网络-输出层设计

前馈神经网络(Feedforward Neural Network)&#xff0c;之前介绍的单层感知机、多层感知机等都属于前馈神经网络&#xff0c;它之所以称为前馈(Feedforward)&#xff0c;或许与其信息往前流有关&#xff1a;数据从输入开始&#xff0c;流过中间计算过程&#xff0c;最后达到输出…

springboot-内置Tomcat

一、springboot的特性之一 基于springboot的特性 自动装配Configuretion 注解 二、springboot内置Tomcat步骤 直接看SpringApplication方法的代码块 总纲&#xff1a; 1、在SpringApplication.run 初始化了一个上下文ConfigurableApplicationContext configurableApplica…

服务负载均衡Ribbon

服务负载均衡Ribbon Ribbon 介绍Ribbon 案例Ribbon 负载均衡策略Ribbon 负载均衡算法设置自定义负载均衡算法 Ribbon 介绍 Ribbon 是一个的客服端负载均衡工具&#xff0c;它是基于 Netflix Ribbon 实现的。它不像 Spring Cloud 服务注册中心、配置中心、API 网关那样独立部署…

使用Postman创建Mock Server

这篇文章将教会大家如何利用 Postman&#xff0c;通过 Mock 的方式测试我们的 API。 什么是 Mock Mock 是一项特殊的测试技巧&#xff0c;可以在没有依赖项的情况下进行单元测试。通常情况下&#xff0c;Mock 与其他方法的主要区别就是&#xff0c;用于取代代码依赖项的模拟对…

论文笔记 | 谷歌 Soft Prompt Learning ,Prefix-Tuning的 -> soft promt -> p tuning v2

论文笔记 | 谷歌 Soft Prompt Learning ptuning -> Prefix-Tuning -> soft promt -> p tuning v2 "The Power of Scale for Parameter-Efficient Prompt Tuning" EMNLP 2021 Google Brain 人能理解的不一定是模型需要的&#xff0c;所以不如让模型自己训…

多线程之线程安全

写在前面 本文一起看下线程安全相关内容。 1&#xff1a;重要的概念 1.1&#xff1a;竞态条件 多个线程竞争同一资源&#xff0c;如果是对多个线程访问资源的顺序敏感&#xff08;即导致非预期结果&#xff09;&#xff0c;则该资源就是竞态条件。 1.2&#xff1a;临界区 …

亚毫秒GC暂停到底有多香?JDK17+ZGC初体验|得物技术

1 前言 垃圾回收器的暂停问题一直是Java工程师关注的重点&#xff0c;特别是对实时响应要求较高的服务来说&#xff0c;CMS和G1等主流垃圾回收器的数十毫秒乃至上百毫秒的暂停时间相当致命。此外&#xff0c;调优门槛也相对较高&#xff0c;需要对垃圾回收器的内部机制有一定的…

Nodejs六、数据库操作

零、文章目录 Nodejs六、数据库操作 1、MYSQL数据库 MYSQL相关知识请参考MYSQL基础 2、在项目中操作 MySQL &#xff08;1&#xff09;操作数据库的步骤 安装操作 MySQL 数据库的第三方模块&#xff08;mysql&#xff09;通过 mysql 模块连接到 MySQL 数据库通过 mysql 模…

chatgpt赋能python:Python编写网站的SEO指南

Python 编写网站的 SEO 指南 Python 是一个高可扩展性和灵活性的编程语言&#xff0c;在创建面向 Web 的应用程序和网站时非常强大。但是&#xff0c;即使你创建了一个出色的网站&#xff0c;也需要将它放在正确的地方以便被人们发现。 搜索引擎优化&#xff08;SEO&#xff0…

【深度学习】2-2 神经网络 - 前向传播实现3层神经网络

神经网络分层 神经网络的一个重要性质是它可以自动地从数据中学习到合适的权重参数。 用图来表示神经网络的话&#xff0c;把最左边的一列称为输入层&#xff0c;最右边的一列称为输出层&#xff0c;中间的一列称为中间层。中间层有时也叫隐藏层&#xff08;或隐含层&#xf…

深入了解计算机SNMP协议:原理、功能和应用场景

前言 简单网络管理协议&#xff08;SNMP&#xff09;是一种用于管理网络设备的协议&#xff0c;它可以让管理员通过网络对设备进行监控、配置和故障排除等操作。本文将详细介绍SNMP的版本、管理信息库MIB、管理信息结构&#xff08;SMI&#xff09;、SNMP报文、5种协议数据单元…

avive零头撸矿

Avive 是一个透明的、自下而上替代自上而下的多元网络&#xff0c;旨在克服当前生态系统的局限性&#xff0c;实现去中心化社会。 aVive&#xff1a;一个基于 SBT 和市场的 deSoc&#xff0c;它使 dapps 能够与分散的位置 oracle 和 SBT 关系进行互操作。您的主权社交网络元宇宙…

Vue中如何进行表单验证码与滑动验证?

Vue中如何进行表单验证码与滑动验证&#xff1f; 在Web应用程序中&#xff0c;表单验证码和滑动验证是常见的安全机制&#xff0c;用于防止恶意攻击和机器人攻击。在Vue中&#xff0c;我们可以使用许多不同的库来实现这些功能。本文将介绍如何使用Vue和vue-verify-code库来实现…

docker中部署lnmp架构

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 docker中部署lnmp架构 前言一、安装docker和docker-compose二、文件部署准备三、创建ngixn虚拟主机配置文件四、创建html文件夹五、启动容器文件结构 前言 Docker是一种轻量…

5.6.1 Ext JS之标签页的关闭和批量关闭

Tab Panel 是包含多个标签页的面板, 这是一种很常用的组件, 类似于浏览器的标签页。关于 Ext JS的Tab Panel的基本使用可以参考: [Ext JS3.9] 标签面板(TabPanel )介绍与开发, 本篇介绍如何关闭单个标签页和批量关闭标签页。 Tab 标签页的可关闭 默认状况下,标签页是无…

23.反射(reflection)|Java学习笔记

文章目录 反射机制Java反射机制原理图Java反射机制可以完成反射相关的主要类反射优点和缺点 Class类 反射机制 一个简单的例子&#xff1a; package com.edu.reflection.question;import java.io.FileInputStream; import java.io.IOException; import java.lang.reflect.Invo…

消防安全知识答题活动小程序v5.0-支持答题后抽奖

关于答题抽奖活动小程序的设计思考 1. 功能设计&#xff1a;作为答题抽奖活动小程序&#xff0c;核心功能应包括答题和抽奖两部分。用户通过答题获取抽奖机会&#xff0c;答题可以设置为多个题目&#xff0c;用户回答正确则获得相应分数。在用户答完问题后&#xff0c;可以立即…