Python的进程和线程——一些基础概念
1. 线程和进程
1.1 线程和进程
进程可以包含多个并行运行的线程;
通常,操作系统创建和管理线程比进程更省CPU资源;
线程用于一些小任务,进程用于繁重的任务;
同一个进程下的线程共享地址空间和其他资源,进程之间相互独立;
1.2 线程 v.s. 进程:
1.2.1 多线程
- 多线程是在同一进程内部创建多个线程来执行任务的技术。每个线程共享相同的内存空间,因此可以轻松共享数据。
- 多线程适合于
I/O 密集型任务,如网络请求、文件操作等,因为线程之间的切换开销较小。 - Python 中的多线程通常由
threading模块实现。
1.2.2 多进程
- 多进程是在操作系统级别创建多个独立的进程来执行任务的技术。每个进程都有自己独立的内存空间,数据不共享,需要通过 IPC(进程间通信)来传递数据。
- 多进程适合于
CPU 密集型任务,如数值计算、图像处理等,因为多进程能够充分利用多核处理器的能力。 - Python 中的多进程通常由
multiprocessing模块实现。
1.2.3 区别
内存和资源开销:多线程共享同一进程的内存空间,因此创建线程的开销较小;而多进程每个进程都有独立的内存空间,创建进程的开销较大。
数据共享:多线程可以轻松共享数据,因为它们共享相同的内存空间;而多进程需要通过 IPC 来传递数据,因为它们的内存空间是独立的。
CPU 利用率:多线程适合于 I/O 密集型任务,因为线程切换开销较小;多进程适合于 CPU 密集型任务,因为可以充分利用多核处理器的能力。
总的来说,多线程适合于需要频繁进行 I/O 操作的任务,而多进程适合于需要大量 CPU 计算的任务。
2. I/O密集型任务和CPU密集型任务
在计算机编程中,任务通常可以根据它们是如何使用计算机资源的来分类。主要有两种类型:I/O 密集型任务和 CPU 密集型任务。
2.1 I/O 密集型任务
I/O 是 Input/Output(输入/输出)的缩写。当说到 I/O 密集型任务时,我们通常是指那些需要大量数据输入和输出的任务。这些任务往往涉及到磁盘、网络或其他类型的数据传输。
特点:
- 等待时间: I/O 操作往往需要等待,比如等待磁盘旋转到正确的位置来读取数据,或者等待网络上的数据包到达。
- CPU 使用率: 在等待数据传输的过程中,CPU 往往是空闲的,因为它不能在没有数据的情况下继续工作。
- 例子: 比如,从一个很大的文件中读取数据,或者从互联网上下载一个文件。这些操作大部分时间都在等待数据到来,而不是在进行大量的计算。
2.2 CPU 密集型任务
CPU 密集型任务是指那些需要大量计算的任务,它们会充分利用 CPU 的计算能力。
特点:
- 计算量大: 这类任务涉及复杂的计算,如图形处理、数据分析、加密解密等。
- CPU 使用率: 在执行这类任务时,CPU 通常会保持高使用率,因为它一直在进行计算。
- 例子: 比如,对一张高分辨率图片进行复杂的图像处理,或者计算一个大型数据集的统计分析。这些任务需要 CPU 进行大量的数学运算。
想象一下,你在厨房里准备晚餐。I/O 密集型任务就像是你在等待水烧开或者烤箱预热。在这期间,你基本上无事可做,只能等待。CPU 密集型任务则像你在切菜、拌沙拉或进行烹饪,你需要不断地工作,直到完成。
在编程中,我们选择多线程、多进程或其他并发模型,取决于任务的类型。如果任务主要是 I/O 密集型的,那么多线程可能会有所帮助,因为线程可以在等待 I/O 操作完成时被操作系统挂起,让其他线程运行。而如果任务是 CPU 密集型的,那么多进程可能更合适,因为每个进程可以在 CPU 上真正地并行运行,从而实现更快的处理速度。
3. 主线程和子线程
在多线程编程中,主线程(Main Thread) 和子线程(Child Thread) 是用来描述线程之间关系的术语。
3.1 主线程(Main Thread)
- 定义: 主线程通常指的是程序启动时创建的第一个线程,它是执行程序主流程的线程。
- 作用: 主线程负责程序的主要逻辑和控制流程,包括启动其他线程(子线程)和管理它们。
- 生命周期: 通常,主线程会持续运行直到程序的所有任务完成,或者直到它被显式终止。
3.2 子线程(Child Thread)
- 定义: 子线程是指由主线程创建的额外线程,用于并行执行任务。
- 作用: 子线程可以用于执行特定的任务,如后台处理、I/O 操作、计算密集型任务等,以提高程序的效率和响应性。
- 生命周期: 子线程的生命周期通常依赖于它们所执行的任务。一旦任务完成,子线程就会结束。
3.3 主线程和子线程的关系
- 创建: 主线程可以创建一个或多个子线程。
- 同步: 主线程可能会等待(通过
join()方法)子线程完成,以确保在继续执行之前子线程的任务已经结束。 - 协作: 主线程和子线程可以共享数据和资源,但需要同步机制(如锁)来避免竞态条件和数据不一致。
- 结束: 主线程的结束通常意味着程序的结束,除非它等待子线程完成。子线程的结束仅表示它们所负责的任务已经完成。
3.4 示例
假设你有一个 Python 程序,它使用 threading 模块创建了多个线程:
import threading
import time
def child_thread_task(name):
print(f"Child thread {name} is running.")
time.sleep(1)
print(f"Child thread {name} has finished.")
def main_thread_task():
print("Main thread is setting up child threads.")
threads = []
for i in range(3):
t = threading.Thread(target=child_thread_task, args=(i,))
threads.append(t)
t.start()
# 主线程等待所有子线程完成
for t in threads:
t.join()
print("All child threads have completed. Main thread is finishing.")
if __name__ == "__main__":
main_thread_task() # 主线程的任务
[Run]
Main thread is setting up child threads.
Child thread 0 is running.
Child thread 1 is running.
Child thread 2 is running.
Child thread 2 has finished.Child thread 1 has finished.
Child thread 0 has finished.
All child threads have completed. Main thread is finishing.
在这个示例中:
main_thread_task()函数中的代码运行在主线程上,它负责创建和启动子线程。child_thread_task()函数中的代码运行在子线程上,每个子线程执行一个独立的任务。- 主线程使用
join()方法等待所有子线程完成后才结束。
4. 锁
4.1 什么是锁
在计算机编程中,锁是一种用来控制对共享资源(如内存、文件等)访问的机制。当多个线程(程序执行的独立流)需要同时运行时,锁可以帮助避免它们在不适当的时刻相互干扰。
4.2 为什么要使用锁
想象一下,如果有多个小孩同时在一个图书馆里,他们都想写同一本书。如果没有规则,他们可能会相互覆盖对方的内容,导致书变得乱七八糟。锁就像是图书馆的规则,它确保一次只有一个人能够写这本书。
在计算机中,当多个线程需要修改同一个变量时,如果不使用锁,就可能出现类似的情况,这被称为 “竞态条件” 。竞态条件会导致程序的行为变得不可预测,因为最终结果取决于线程执行的顺序,而这个顺序是无法控制的。
4.3 锁是如何工作的
锁的工作原理很简单:
获取锁: 当一个线程想要修改一个共享资源时,它首先需要“获取”(或“锁定”)一个锁。这通常通过调用
acquire()方法来实现。执行操作: 一旦线程获取了锁,它就可以安全地执行对共享资源的修改,因为其他线程必须等待锁被释放。
释放锁: 修改完成后,线程会“释放”(或“解锁”)锁,这通常通过调用
release()方法来实现。这样,其他等待锁的线程就可以继续执行。
4.4 锁的类型
互斥锁(Mutex Lock): 最常见的锁类型,一次只允许一个线程获取锁。
递归锁(Recursive Lock): 允许同一个线程多次获取同一个锁,避免了递归函数中的死锁问题。
读写锁(RWLock): 允许多个读取操作同时进行,但写操作是排他的。
信号量(Semaphore): 类似于锁,但可以设定一个计数值,允许多个线程同时访问资源。
条件锁(Condition): 允许线程在某个条件成立之前挂起,直到其他线程发出信号。
4.5 使用锁的例子
假设我们有一个变量 counter,我们希望两个线程能够交替地对其进行加一操作。
import threading
counter = 0
lock = threading.Lock()
def increment():
global counter
for _ in range(100000):
lock.acquire() # 获取锁
counter += 1
lock.release() # 释放锁
thread1 = threading.Thread(target=increment)
thread2 = threading.Thread(target=increment)
thread1.start()
thread2.start()
thread1.join()
thread2.join()
print(counter) # 应该打印 200000
在这个例子中,lock 确保了 counter 的增加操作是安全的,即使两个线程在同时运行。如果没有 lock,两个线程可能会同时读取和修改 counter,导致最终结果不正确。
4.6 注意事项:
死锁: 如果不正确地使用锁,可能会导致死锁,即两个或多个线程相互等待对方释放锁,但没有一个线程愿意放弃,导致程序停止响应。
性能: 过度使用锁可能会降低程序的性能,因为线程需要等待获取锁。
避免锁: 在可能的情况下,尽量避免使用共享资源,或者使用线程局部数据,这样可以减少对锁的需求。
正确地使用锁对于避免竞态条件和确保程序的正确性至关重要。
Python的进程和线程——一些基础概念的更多相关文章
- Python的进程与线程--思维导图
Python的进程与线程--思维导图
- 【python】-- 进程与线程
进程与线程 一.概念 1.简述: 计算机,所有的指令的操作都是有CPU来负责的,cpu是来负责运算的.OS(操作系统) 调度cpu的最小单位就是线程.程序启动后,从内存中分一块空间,把数据临时存在内存 ...
- Python创建进程、线程的两种方式
代码创建进程和线程的两种方式 """ 定心丸:Python创建进程和线程的方式基本都是一致的,包括其中的调用方法等,学会一个 另一个自然也就会了. "" ...
- python之进程与线程
什么是操作系统 可能很多人都会说,我们平时装的windows7 windows10都是操作系统,没错,他们都是操作系统.还有没有其他的? 想想我们使用的手机,Google公司的Androi ...
- 《Python》进程收尾线程初识
一.数据共享 from multiprocessing import Manager 把所有实现了数据共享的比较便捷的类都重新又封装了一遍,并且在原有的multiprocessing基础上增加了新的机 ...
- python的进程与线程(二)
线程 之前了解了操作系统的发展史,也知道了进程和线程的概念,归纳一下就是: 进程:本质上就是一段程序的运行过程(抽象的概念) 线程:最小的执行单元,是进程的实体 ...
- Python 9 进程,线程
本节内容 python GIL全局解释器锁 线程 进程 Python GIL(Global Interpreter Lock) In CPython, the global interpreter l ...
- python之进程和线程
1 操作系统 为什么要有操作系统 ? 操作系统位于底层硬件与应用软件之间的一层 工作方式:向下管理硬件,向上提供接口 操作系统进程切换: 出现IO操作 固定时间 2 进程和线程的概念 进程就是一个程序 ...
- Python中进程和线程的总体区别
Num01–>线程 线程是操作系统中能够进行运算调度的最小单位.它被包含在进程之中,是进程中的实际运作单位. 一个线程指的是进程中一个单一顺序的控制流. 一个进程中可以并发多条线程,每条线程并行 ...
- Python的进程、线程和threading模块
(注:本文部分内容摘自互联网,由于作者水平有限,不足之处,还望留言指正.) 怀念在学校念书的时候,我不小心触碰到了错误,老师会说:你错了:而我却总是倔强得以为自己没错.我的内心是不屑的,直到在真理面前 ...
随机推荐
- Flutter笔记 - 事件分发
事件处理流程 Flutter 事件处理流程主要分两步,为了聚焦核心流程,我们以用户触摸事件为例来说明: 命中测试:当手指按下时,触发 PointerDownEvent 事件,按照深度优先遍历当前渲染( ...
- spring-jdbc5新特性,一个配置文件解决临时修改数据库的问题
import java.sql.SQLException; import java.util.List; import java.util.Map; import javax.sql.DataSour ...
- 力扣12(java)-整数转罗马数字(中等)
题目: 罗马数字包含以下七种字符:I, V, X, L, C, D, M 1 字符 数值 2 I 1 3 V 5 4 X 10 5 L 50 6 C 100 7 D 500 8 M 1000 例如, ...
- 关于 Data Lake 的概念、架构与应用场景介绍
数据湖(Data Lake)概念介绍 什么是数据湖(Data Lake)? 数据湖的起源,应该追溯到2010年10月,由 Pentaho 的创始人兼 CTO, James Dixon 所提出,他提出的 ...
- 阿里云实时数仓Hologres年度发布,解读数仓新趋势
简介:阿里云实时数仓Hologres年度发布,解读数仓新趋势. 1月7日,阿里云实时数仓Hologres发布最新版本,在成本.可用性.在线高可用等多方面进行了能力升级,行存吞吐提升100%,列存吞吐 ...
- 新型DDoS来袭 | 基于STUN协议的DDoS反射攻击分析
简介: 作为新型反射类型,目前仍存绕过防御可能性. 阿里云安全近期发现利用STUN(Session Traversal Utilities for NAT,NAT会话穿越应用程序)服务发起的DDoS反 ...
- 工业视觉智能实战经验之IVI算法框架2.0
简介: 工业视觉智能团队在交付了多个工业视觉智能质检项目后,发现了工业视觉智能的共性问题和解法,打造了工业视觉智能平台,通过平台的方式积累和提升工业视觉的通用能力.在平台建设上最核心的能力是算法能力 ...
- [FE] 被动检测 iframe 加载 src 成功失败的一种思路和方式 (Vue)
思路:设置定时器一个,n 秒后设置 404 或其它,此时给 iframe 的 onload 事件设置回调函数,加载完成则取消定时器. 示例: data () { return { handler: n ...
- [Go] go build 减小二进制文件大小的几种方式
第一种 是去除不需要的调试信息: go build -ldflags "-s -w" main.go 实测 19M 减小为 15M,幅度 2% 第二种 压缩 UPX: the Ul ...
- WPF 已知问题 InputEventArgs 的 Timestamp 属性是静态的导致事件之间相互影响
本文记录一个 WPF 已知的设计问题,当前此问题已经被大佬修复,这个设计问题刚好属于比较边缘的模块,我写了这么多年的代码还没有踩到这个坑一次,也没有听到有谁提到这个坑 远古时候,不知道大佬是故意还是失 ...