python使用threading多线程

2024-08-30 16:12

本文主要是介绍python使用threading多线程,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

多线程

  • 1 多线程和多进程的速度区别
      • 1. 任务类型
      • 2. 系统架构
      • 3. 编程语言特性
      • 4. 资源开销
      • 5. 应用场景
      • 总结
      • 示例代码
      • 示例:计算阶乘
        • 使用多线程
        • 使用多进程
      • 结论
  • 2 多线程和多进程的特点区别
      • 1. 内存空间
      • 2. 资源开销
      • 3. 并发执行
      • 4. 通信
      • 5. 调度
      • 6. 错误处理
      • 示例代码
        • 示例:多进程
        • 示例:多线程
      • 总结
  • 3 使用多线程的基本步骤
      • 使用多线程的基本步骤
      • 示例:使用多线程
      • 多线程间的变量管理
      • 示例:使用锁来管理多线程间的变量
      • 解释
      • 总结
  • 4 多线程的使用
      • 示例:使用多线程
      • 解释
      • 示例:使用线程类
      • 解释
      • 示例:使用锁进行线程间同步
      • 解释
      • 总结

1 多线程和多进程的速度区别

多线程和多进程在执行速度上的比较取决于多个因素,包括任务类型、系统架构、编程语言特性以及具体的应用场景。下面是一些关键点来帮助理解多线程和多进程在性能方面的区别:

1. 任务类型

  • CPU 密集型任务:这类任务主要涉及大量的计算,如数学运算、图像处理等。在这种情况下,多进程通常更快,因为每个进程可以在单独的 CPU 核心上运行,而多线程在单个进程内可能会受到全局解释器锁(GIL)的影响,尤其是在 Python 这样的语言中。

  • I/O 密集型任务:这类任务主要涉及大量的输入/输出操作,如网络请求、文件读写等。在这种情况下,多线程通常表现得更好,因为线程可以更好地利用等待 I/O 操作的时间来做其他事情,而多进程则需要更多的资源开销来创建和销毁进程。

2. 系统架构

  • 多核处理器:现代计算机通常拥有多个 CPU 核心。多进程可以更好地利用这些核心来并行执行任务,从而提高整体性能。

  • 单核处理器:在单核处理器上,多进程可能不会带来显著的性能提升,因为一次只能运行一个进程。在这种情况下,多线程可能是更好的选择。

3. 编程语言特性

  • 全局解释器锁 (GIL):Python 的 CPython 实现有一个全局解释器锁,它限制了多线程程序在同一时间只能执行一个线程。这意味着对于 CPU 密集型任务,多线程在 Python 中可能不会像预期的那样提高性能。相比之下,多进程不受 GIL 的限制。

4. 资源开销

  • 创建和销毁:进程的创建和销毁通常比线程更耗时。因此,在频繁创建和销毁的情况下,多线程可能比多进程更快。

  • 内存管理:线程共享同一进程的内存空间,这使得线程间的通信更高效。而进程有自己的独立内存空间,进程间的通信通常需要额外的机制,如管道、队列等。

5. 应用场景

  • 短任务:对于非常短暂的任务,使用多线程可能更快,因为线程的创建和销毁开销较小。

  • 长任务:对于需要长时间运行的任务,多进程可能更快,因为进程间的资源隔离可以避免一个任务出错影响其他任务。

总结

  • 多进程 更适合 CPU 密集型任务和需要大量并行计算的任务。
  • 多线程 更适合 I/O 密集型任务和需要在多个任务之间快速切换的任务。

示例代码

为了进一步说明这一点,我们可以编写一个简单的示例来比较多线程和多进程在执行相同任务时的性能差异。

示例:计算阶乘

使用多线程
import threading
import timedef compute_factorial(n):"""计算阶乘"""result = 1for i in range(1, n + 1):result *= iprint(f"Factorial of {n} is {result}")def main_threading():numbers = [100000, 150000, 200000]threads = []start_time = time.time()# 创建线程for number in numbers:thread = threading.Thread(target=compute_factorial, args=(number,))threads.append(thread)thread.start()# 等待所有线程完成for thread in threads:thread.join()end_time = time.time()print(f"Total time taken (threading): {end_time - start_time:.2f} seconds")if __name__ == "__main__":main_threading()
使用多进程
import multiprocessing
import timedef compute_factorial(n):"""计算阶乘"""result = 1for i in range(1, n + 1):result *= iprint(f"Factorial of {n} is {result}")def main_multiprocessing():numbers = [100000, 150000, 200000]processes = []start_time = time.time()# 创建进程for number in numbers:process = multiprocessing.Process(target=compute_factorial, args=(number,))processes.append(process)process.start()# 等待所有进程完成for process in processes:process.join()end_time = time.time()print(f"Total time taken (multiprocessing): {end_time - start_time:.2f} seconds")if __name__ == "__main__":main_multiprocessing()

结论

根据具体的任务类型和环境,多线程和多进程的表现会有很大差异。在选择多线程还是多进程时,需要考虑任务的特点、系统的硬件配置以及编程语言的特性。在大多数情况下,对于 CPU 密集型任务,多进程更有可能提供更好的性能;而对于 I/O 密集型任务,多线程可能更优。

多进程和多线程是并发编程中的两种常见模型,它们各有特点和适用场景。下面是多进程和多线程的主要区别:

2 多线程和多进程的特点区别

1. 内存空间

  • 多进程:每个进程都有自己的独立内存空间。这意味着一个进程中的变量和其他数据不会直接影响到其他进程中的数据。这种隔离有助于避免数据竞争和同步问题,但也增加了进程间通信的复杂性。

  • 多线程:所有线程共享同一进程的内存空间。这意味着所有线程可以访问相同的变量和其他数据。这种共享可以简化线程间的通信,但需要更多的同步机制来避免竞态条件。

2. 资源开销

  • 多进程:创建和销毁进程通常比创建和销毁线程需要更多的系统资源。这是因为每个进程都有自己独立的地址空间、文件描述符等资源。

  • 多线程:创建和销毁线程相对轻量级,消耗的资源较少。这是因为线程共享同一进程的资源。

3. 并发执行

  • 多进程:在多核处理器上,不同的进程可以并行执行在不同的核心上,从而实现真正的并行计算。这对于 CPU 密集型任务非常有用。

  • 多线程:在多核处理器上,线程可以被调度到不同的核心上执行,但由于全局解释器锁(GIL)的存在,对于某些编程语言(如 Python 的 CPython 实现),线程并不能完全并行执行。

4. 通信

  • 多进程:进程间通信(IPC)通常需要使用专门的机制,如管道、队列、共享内存等。

  • 多线程:线程间的通信更加简单,可以直接访问共享数据。

5. 调度

  • 多进程:操作系统负责调度进程。进程的调度通常是基于优先级和时间片的。

  • 多线程:操作系统和编程语言的运行时系统共同负责调度线程。线程的调度通常更加灵活,可以根据线程的状态进行调整。

6. 错误处理

  • 多进程:一个进程中的错误通常不会影响其他进程。这意味着一个进程崩溃不会导致整个程序失败。

  • 多线程:一个线程中的错误可能会影响其他线程,甚至导致整个进程崩溃。因此,需要更多的错误处理机制来确保程序的健壮性。

示例代码

下面是一些简单的示例代码来说明多进程和多线程的区别:

示例:多进程
import multiprocessing
import timedef compute_factorial(n):"""计算阶乘"""result = 1for i in range(1, n + 1):result *= iprint(f"Factorial of {n} is {result}")def main_multiprocessing():numbers = [100000, 150000, 200000]processes = []start_time = time.time()# 创建进程for number in numbers:process = multiprocessing.Process(target=compute_factorial, args=(number,))processes.append(process)process.start()# 等待所有进程完成for process in processes:process.join()end_time = time.time()print(f"Total time taken (multiprocessing): {end_time - start_time:.2f} seconds")if __name__ == "__main__":main_multiprocessing()
示例:多线程
import threading
import timedef compute_factorial(n):"""计算阶乘"""result = 1for i in range(1, n + 1):result *= iprint(f"Factorial of {n} is {result}")def main_threading():numbers = [100000, 150000, 200000]threads = []start_time = time.time()# 创建线程for number in numbers:thread = threading.Thread(target=compute_factorial, args=(number,))threads.append(thread)thread.start()# 等待所有线程完成for thread in threads:thread.join()end_time = time.time()print(f"Total time taken (threading): {end_time - start_time:.2f} seconds")if __name__ == "__main__":main_threading()

总结

多进程和多线程各有优势和局限性。多进程适用于需要大量并行计算的任务,特别是在多核处理器上。多线程适用于需要快速切换任务和 I/O 密集型任务。在选择使用多进程还是多线程时,需要考虑任务的特点、系统的硬件配置以及编程语言的特性。

3 使用多线程的基本步骤

在使用多线程进行编程时,线程间的变量管理非常重要,以避免竞态条件和确保数据的一致性。下面是一些关于如何使用多线程以及如何管理多线程间的变量的基本指南和示例。

使用多线程的基本步骤

  1. 导入模块:在 Python 中,通常使用 threading 模块来创建和管理线程。

  2. 定义线程函数:定义一个函数,该函数将在新线程中执行。

  3. 创建线程:使用 threading.Thread 类创建线程对象,并传递要执行的目标函数及其参数。

  4. 启动线程:调用线程对象的 start() 方法来启动线程。

  5. 等待线程完成:如果需要等待线程完成,可以使用 join() 方法。

示例:使用多线程

import threading
import timedef compute_factorial(n):"""计算阶乘"""result = 1for i in range(1, n + 1):result *= iprint(f"Factorial of {n} is {result}")def main_threading():numbers = [100000, 150000, 200000]threads = []start_time = time.time()# 创建线程for number in numbers:thread = threading.Thread(target=compute_factorial, args=(number,))threads.append(thread)thread.start()# 等待所有线程完成for thread in threads:thread.join()end_time = time.time()print(f"Total time taken (threading): {end_time - start_time:.2f} seconds")if __name__ == "__main__":main_threading()

多线程间的变量管理

在多线程编程中,由于多个线程可能同时访问和修改共享变量,因此需要采取措施来保证数据的一致性和避免竞态条件。

  1. 使用锁:锁是最常见的同步工具之一,可以用来保护临界区,即那些需要被互斥访问的代码段。Python 中可以使用 threading.Lockthreading.RLock

  2. 使用原子操作:对于一些简单的操作,如计数器的增减,可以使用原子操作。Python 中可以使用 threading.atomic()(Python 3.8 及以上版本)或者 threading.Valuethreading.Condition

  3. 使用线程安全的数据结构:Python 的 threading 模块提供了一些线程安全的数据结构,如 threading.Semaphorethreading.Condition

  4. 使用队列:对于需要在线程间传递数据的情况,可以使用 queue 模块中的 Queue,它是线程安全的。

示例:使用锁来管理多线程间的变量

import threading
import timeclass Counter:def __init__(self):self.count = 0self.lock = threading.Lock()def increment(self):with self.lock:self.count += 1print(f"Counter incremented to {self.count}")def worker(counter):for _ in range(1000):counter.increment()def main_threading_with_lock():counter = Counter()threads = []start_time = time.time()# 创建线程for _ in range(5):thread = threading.Thread(target=worker, args=(counter,))threads.append(thread)thread.start()# 等待所有线程完成for thread in threads:thread.join()end_time = time.time()print(f"Total time taken (threading with lock): {end_time - start_time:.2f} seconds")if __name__ == "__main__":main_threading_with_lock()

解释

  1. 定义 Counter 类:我们定义了一个 Counter 类,它包含一个计数器和一个锁。每次调用 increment 方法时都会获取锁,以确保计数器的操作是原子的。

  2. 创建线程:我们在 main_threading_with_lock 函数中创建了多个线程,并将 Counter 对象作为参数传递给每个线程。

  3. 使用锁:在 worker 函数中,我们使用了上下文管理器 with 语句来自动获取和释放锁,这样可以确保在修改计数器时不会发生竞态条件。

总结

在多线程编程中,正确管理线程间的变量是非常重要的。使用锁、原子操作和线程安全的数据结构可以帮助你避免竞态条件,并确保数据的一致性。在实际开发中,根据具体情况选择合适的同步机制是非常关键的。

4 多线程的使用

在使用多线程编程时,通常需要遵循以下几个基本步骤:

  1. 导入模块:在 Python 中,通常使用 threading 模块来创建和管理线程。

  2. 定义线程函数:定义一个函数,该函数将在新线程中执行。

  3. 创建线程:使用 threading.Thread 类创建线程对象,并传递要执行的目标函数及其参数。

  4. 启动线程:调用线程对象的 start() 方法来启动线程。

  5. 等待线程完成:如果需要等待线程完成,可以使用 join() 方法。

示例:使用多线程

下面是一个简单的示例,演示如何使用多线程来执行任务。

import threading
import timedef compute_factorial(n):"""计算阶乘"""result = 1for i in range(1, n + 1):result *= iprint(f"Factorial of {n} is {result}")def main_threading():numbers = [100000, 150000, 200000]threads = []start_time = time.time()# 创建线程for number in numbers:thread = threading.Thread(target=compute_factorial, args=(number,))threads.append(thread)thread.start()# 等待所有线程完成for thread in threads:thread.join()end_time = time.time()print(f"Total time taken (threading): {end_time - start_time:.2f} seconds")if __name__ == "__main__":main_threading()

解释

  1. 定义 compute_factorial 函数:这个函数用于计算一个整数的阶乘。
  2. 创建线程:在 main_threading 函数中,我们为每个要计算的数字创建了一个线程,并启动了这些线程。
  3. 等待线程完成:使用 thread.join() 方法来等待每个线程完成。

示例:使用线程类

除了直接使用 threading.Thread 类外,还可以通过继承 Thread 类来自定义线程类。这种方式可以让线程的管理更加灵活。

import threading
import timeclass ComputeFactorialThread(threading.Thread):def __init__(self, number):super().__init__()self.number = numberdef run(self):"""计算阶乘"""result = 1for i in range(1, self.number + 1):result *= iprint(f"Factorial of {self.number} is {result}")def main_threading_with_class():numbers = [100000, 150000, 200000]threads = []start_time = time.time()# 创建线程for number in numbers:thread = ComputeFactorialThread(number)threads.append(thread)thread.start()# 等待所有线程完成for thread in threads:thread.join()end_time = time.time()print(f"Total time taken (threading with class): {end_time - start_time:.2f} seconds")if __name__ == "__main__":main_threading_with_class()

解释

  1. 定义 ComputeFactorialThread 类:我们定义了一个新的线程类 ComputeFactorialThread,它继承自 threading.Thread
  2. 重写 run 方法:在 ComputeFactorialThread 类中,我们重写了 run 方法来定义线程执行的具体任务。
  3. 创建线程:在 main_threading_with_class 函数中,我们创建了多个 ComputeFactorialThread 对象,并启动了这些线程。
  4. 等待线程完成:使用 thread.join() 方法来等待每个线程完成。

示例:使用锁进行线程间同步

在多线程环境中,为了避免竞态条件,通常需要使用锁来同步线程间的操作。

import threading
import timeclass SharedCounter:def __init__(self):self.count = 0self.lock = threading.Lock()def increment(self):with self.lock:self.count += 1print(f"Counter incremented to {self.count}")def worker(counter):for _ in range(1000):counter.increment()def main_threading_with_lock():counter = SharedCounter()threads = []start_time = time.time()# 创建线程for _ in range(5):thread = threading.Thread(target=worker, args=(counter,))threads.append(thread)thread.start()# 等待所有线程完成for thread in threads:thread.join()end_time = time.time()print(f"Total time taken (threading with lock): {end_time - start_time:.2f} seconds")if __name__ == "__main__":main_threading_with_lock()

解释

  1. 定义 SharedCounter 类:我们定义了一个 SharedCounter 类,它包含一个计数器和一个锁。每次调用 increment 方法时都会获取锁,以确保计数器的操作是原子的。
  2. 创建线程:我们在 main_threading_with_lock 函数中创建了多个线程,并将 SharedCounter 对象作为参数传递给每个线程。
  3. 使用锁:在 worker 函数中,我们使用了上下文管理器 with 语句来自动获取和释放锁,这样可以确保在修改计数器时不会发生竞态条件。

总结

多线程编程允许你同时执行多个任务,提高程序的效率。在实际开发中,根据具体情况选择合适的同步机制是非常关键的。使用锁、原子操作和线程安全的数据结构可以帮助你避免竞态条件,并确保数据的一致性。

这篇关于python使用threading多线程的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1121305

相关文章

使用Python删除Excel中的行列和单元格示例详解

《使用Python删除Excel中的行列和单元格示例详解》在处理Excel数据时,删除不需要的行、列或单元格是一项常见且必要的操作,本文将使用Python脚本实现对Excel表格的高效自动化处理,感兴... 目录开发环境准备使用 python 删除 Excphpel 表格中的行删除特定行删除空白行删除含指定

深入理解Go语言中二维切片的使用

《深入理解Go语言中二维切片的使用》本文深入讲解了Go语言中二维切片的概念与应用,用于表示矩阵、表格等二维数据结构,文中通过示例代码介绍的非常详细,需要的朋友们下面随着小编来一起学习学习吧... 目录引言二维切片的基本概念定义创建二维切片二维切片的操作访问元素修改元素遍历二维切片二维切片的动态调整追加行动态

prometheus如何使用pushgateway监控网路丢包

《prometheus如何使用pushgateway监控网路丢包》:本文主要介绍prometheus如何使用pushgateway监控网路丢包问题,具有很好的参考价值,希望对大家有所帮助,如有错误... 目录监控网路丢包脚本数据图表总结监控网路丢包脚本[root@gtcq-gt-monitor-prome

Python通用唯一标识符模块uuid使用案例详解

《Python通用唯一标识符模块uuid使用案例详解》Pythonuuid模块用于生成128位全局唯一标识符,支持UUID1-5版本,适用于分布式系统、数据库主键等场景,需注意隐私、碰撞概率及存储优... 目录简介核心功能1. UUID版本2. UUID属性3. 命名空间使用场景1. 生成唯一标识符2. 数

SpringBoot中如何使用Assert进行断言校验

《SpringBoot中如何使用Assert进行断言校验》Java提供了内置的assert机制,而Spring框架也提供了更强大的Assert工具类来帮助开发者进行参数校验和状态检查,下... 目录前言一、Java 原生assert简介1.1 使用方式1.2 示例代码1.3 优缺点分析二、Spring Fr

Python办公自动化实战之打造智能邮件发送工具

《Python办公自动化实战之打造智能邮件发送工具》在数字化办公场景中,邮件自动化是提升工作效率的关键技能,本文将演示如何使用Python的smtplib和email库构建一个支持图文混排,多附件,多... 目录前言一、基础配置:搭建邮件发送框架1.1 邮箱服务准备1.2 核心库导入1.3 基础发送函数二、

Android kotlin中 Channel 和 Flow 的区别和选择使用场景分析

《Androidkotlin中Channel和Flow的区别和选择使用场景分析》Kotlin协程中,Flow是冷数据流,按需触发,适合响应式数据处理;Channel是热数据流,持续发送,支持... 目录一、基本概念界定FlowChannel二、核心特性对比数据生产触发条件生产与消费的关系背压处理机制生命周期

java使用protobuf-maven-plugin的插件编译proto文件详解

《java使用protobuf-maven-plugin的插件编译proto文件详解》:本文主要介绍java使用protobuf-maven-plugin的插件编译proto文件,具有很好的参考价... 目录protobuf文件作为数据传输和存储的协议主要介绍在Java使用maven编译proto文件的插件

Javaee多线程之进程和线程之间的区别和联系(最新整理)

《Javaee多线程之进程和线程之间的区别和联系(最新整理)》进程是资源分配单位,线程是调度执行单位,共享资源更高效,创建线程五种方式:继承Thread、Runnable接口、匿名类、lambda,r... 目录进程和线程进程线程进程和线程的区别创建线程的五种写法继承Thread,重写run实现Runnab

Python包管理工具pip的升级指南

《Python包管理工具pip的升级指南》本文全面探讨Python包管理工具pip的升级策略,从基础升级方法到高级技巧,涵盖不同操作系统环境下的最佳实践,我们将深入分析pip的工作原理,介绍多种升级方... 目录1. 背景介绍1.1 目的和范围1.2 预期读者1.3 文档结构概述1.4 术语表1.4.1 核