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

相关文章

Linux使用fdisk进行磁盘的相关操作

《Linux使用fdisk进行磁盘的相关操作》fdisk命令是Linux中用于管理磁盘分区的强大文本实用程序,这篇文章主要为大家详细介绍了如何使用fdisk进行磁盘的相关操作,需要的可以了解下... 目录简介基本语法示例用法列出所有分区查看指定磁盘的区分管理指定的磁盘进入交互式模式创建一个新的分区删除一个存

C#使用HttpClient进行Post请求出现超时问题的解决及优化

《C#使用HttpClient进行Post请求出现超时问题的解决及优化》最近我的控制台程序发现有时候总是出现请求超时等问题,通常好几分钟最多只有3-4个请求,在使用apipost发现并发10个5分钟也... 目录优化结论单例HttpClient连接池耗尽和并发并发异步最终优化后优化结论我直接上优化结论吧,

SpringBoot使用Apache Tika检测敏感信息

《SpringBoot使用ApacheTika检测敏感信息》ApacheTika是一个功能强大的内容分析工具,它能够从多种文件格式中提取文本、元数据以及其他结构化信息,下面我们来看看如何使用Ap... 目录Tika 主要特性1. 多格式支持2. 自动文件类型检测3. 文本和元数据提取4. 支持 OCR(光学

JAVA系统中Spring Boot应用程序的配置文件application.yml使用详解

《JAVA系统中SpringBoot应用程序的配置文件application.yml使用详解》:本文主要介绍JAVA系统中SpringBoot应用程序的配置文件application.yml的... 目录文件路径文件内容解释1. Server 配置2. Spring 配置3. Logging 配置4. Ma

Python MySQL如何通过Binlog获取变更记录恢复数据

《PythonMySQL如何通过Binlog获取变更记录恢复数据》本文介绍了如何使用Python和pymysqlreplication库通过MySQL的二进制日志(Binlog)获取数据库的变更记录... 目录python mysql通过Binlog获取变更记录恢复数据1.安装pymysqlreplicat

利用Python编写一个简单的聊天机器人

《利用Python编写一个简单的聊天机器人》这篇文章主要为大家详细介绍了如何利用Python编写一个简单的聊天机器人,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 使用 python 编写一个简单的聊天机器人可以从最基础的逻辑开始,然后逐步加入更复杂的功能。这里我们将先实现一个简单的

Linux使用dd命令来复制和转换数据的操作方法

《Linux使用dd命令来复制和转换数据的操作方法》Linux中的dd命令是一个功能强大的数据复制和转换实用程序,它以较低级别运行,通常用于创建可启动的USB驱动器、克隆磁盘和生成随机数据等任务,本文... 目录简介功能和能力语法常用选项示例用法基础用法创建可启动www.chinasem.cn的 USB 驱动

基于Python开发电脑定时关机工具

《基于Python开发电脑定时关机工具》这篇文章主要为大家详细介绍了如何基于Python开发一个电脑定时关机工具,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录1. 简介2. 运行效果3. 相关源码1. 简介这个程序就像一个“忠实的管家”,帮你按时关掉电脑,而且全程不需要你多做

C#使用yield关键字实现提升迭代性能与效率

《C#使用yield关键字实现提升迭代性能与效率》yield关键字在C#中简化了数据迭代的方式,实现了按需生成数据,自动维护迭代状态,本文主要来聊聊如何使用yield关键字实现提升迭代性能与效率,感兴... 目录前言传统迭代和yield迭代方式对比yield延迟加载按需获取数据yield break显式示迭

Python实现高效地读写大型文件

《Python实现高效地读写大型文件》Python如何读写的是大型文件,有没有什么方法来提高效率呢,这篇文章就来和大家聊聊如何在Python中高效地读写大型文件,需要的可以了解下... 目录一、逐行读取大型文件二、分块读取大型文件三、使用 mmap 模块进行内存映射文件操作(适用于大文件)四、使用 pand