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

相关文章

Django开发时如何避免频繁发送短信验证码(python图文代码)

《Django开发时如何避免频繁发送短信验证码(python图文代码)》Django开发时,为防止频繁发送验证码,后端需用Redis限制请求频率,结合管道技术提升效率,通过生产者消费者模式解耦业务逻辑... 目录避免频繁发送 验证码1. www.chinasem.cn避免频繁发送 验证码逻辑分析2. 避免频繁

Java使用Thumbnailator库实现图片处理与压缩功能

《Java使用Thumbnailator库实现图片处理与压缩功能》Thumbnailator是高性能Java图像处理库,支持缩放、旋转、水印添加、裁剪及格式转换,提供易用API和性能优化,适合Web应... 目录1. 图片处理库Thumbnailator介绍2. 基本和指定大小图片缩放功能2.1 图片缩放的

精选20个好玩又实用的的Python实战项目(有图文代码)

《精选20个好玩又实用的的Python实战项目(有图文代码)》文章介绍了20个实用Python项目,涵盖游戏开发、工具应用、图像处理、机器学习等,使用Tkinter、PIL、OpenCV、Kivy等库... 目录① 猜字游戏② 闹钟③ 骰子模拟器④ 二维码⑤ 语言检测⑥ 加密和解密⑦ URL缩短⑧ 音乐播放

python panda库从基础到高级操作分析

《pythonpanda库从基础到高级操作分析》本文介绍了Pandas库的核心功能,包括处理结构化数据的Series和DataFrame数据结构,数据读取、清洗、分组聚合、合并、时间序列分析及大数据... 目录1. Pandas 概述2. 基本操作:数据读取与查看3. 索引操作:精准定位数据4. Group

Python pandas库自学超详细教程

《Pythonpandas库自学超详细教程》文章介绍了Pandas库的基本功能、安装方法及核心操作,涵盖数据导入(CSV/Excel等)、数据结构(Series、DataFrame)、数据清洗、转换... 目录一、什么是Pandas库(1)、Pandas 应用(2)、Pandas 功能(3)、数据结构二、安

Python使用Tenacity一行代码实现自动重试详解

《Python使用Tenacity一行代码实现自动重试详解》tenacity是一个专为Python设计的通用重试库,它的核心理念就是用简单、清晰的方式,为任何可能失败的操作添加重试能力,下面我们就来看... 目录一切始于一个简单的 API 调用Tenacity 入门:一行代码实现优雅重试精细控制:让重试按我

Python安装Pandas库的两种方法

《Python安装Pandas库的两种方法》本文介绍了三种安装PythonPandas库的方法,通过cmd命令行安装并解决版本冲突,手动下载whl文件安装,更换国内镜像源加速下载,最后建议用pipli... 目录方法一:cmd命令行执行pip install pandas方法二:找到pandas下载库,然后

MySQL中EXISTS与IN用法使用与对比分析

《MySQL中EXISTS与IN用法使用与对比分析》在MySQL中,EXISTS和IN都用于子查询中根据另一个查询的结果来过滤主查询的记录,本文将基于工作原理、效率和应用场景进行全面对比... 目录一、基本用法详解1. IN 运算符2. EXISTS 运算符二、EXISTS 与 IN 的选择策略三、性能对比

Python实现网格交易策略的过程

《Python实现网格交易策略的过程》本文讲解Python网格交易策略,利用ccxt获取加密货币数据及backtrader回测,通过设定网格节点,低买高卖获利,适合震荡行情,下面跟我一起看看我们的第一... 网格交易是一种经典的量化交易策略,其核心思想是在价格上下预设多个“网格”,当价格触发特定网格时执行买

Python标准库之数据压缩和存档的应用详解

《Python标准库之数据压缩和存档的应用详解》在数据处理与存储领域,压缩和存档是提升效率的关键技术,Python标准库提供了一套完整的工具链,下面小编就来和大家简单介绍一下吧... 目录一、核心模块架构与设计哲学二、关键模块深度解析1.tarfile:专业级归档工具2.zipfile:跨平台归档首选3.