线程与进程的关系
概念:
线程的进程的一部分
进程是正在运行的程序实体
一个进程包含多个线程
进程是程序中的资源调度的一个最小单位:一个进程可以并发多个线程,多个线程做不同的任务
不同的进程之间资源不共享,但是一个进程中的多个线程资源完全共享
多线程会导致资源冲突: win一般采用 多线程
多进程会导致资源占用不足: Linux不支持多线程, 只采用重量级多进程
并发和并行:
并发:通过cpu的调度,让用户看上去同时执行,实际上cpu操作层面不是真正的同时(多线程同时操作
并行: 多个cpu实例或者多台机器同时执行
python 全局锁 GIL(Global Interpreter Lock)
因此:不管如何设置多线程,都是并发而不是并行
故此: python的多线程编程技术只适合IO密集型的任务, 不适合计算型(cpu应用多)的任务
IO密集型: 频繁的读写数据的场景叫 IO密集型的任务,如 增删改查
计算型 : 频繁调用cpu计算的场景,叫计算密集型的场景, 如 算法逻辑运算
解决GIL的办法: 多进程的技术
python实现多线程的几种方式:
thread:模块提供了基本的线程和互斥锁的支持,更底层的实现模块
def start_new_thread (function, args, kwargs=None): 开启一个新的线程, 参数跟方法名字 ,元祖, 可选参数可以为空
threading: threading封装了thread,提供了更加全面的线程使用方法,是更高级的线程实现模块
name 线程名
ident 线程标志
daemon 表示是否是守护线程
__init__(group=None, tatget=None,args= (), kwargs ={}, verbose=None, daemon=None) 实例化一个线程对象,需要有一个可 调用的 target
start() 开始执行该线程
run() 线程启动时,运行的函数,通常会被 重写
join (timeout=None) 阻塞,直到达到timeout或者其他线 程执行完毕
getName() 返回线程名 设置线程名 线程是否存活 判断是否是守护线程
setName (name)
isAlivel /is_alive ()
isDaemon()
setDaemon(daemonic) 把线程的守护标志设定为True或者 False (必须在线程 start()之前调 用)
锁源于对象,就是把线程执行任务过程中的多个指令变成不可以被分割的,从而锁定任务, 让其他线程无法访问。
守护线程与非守护线程
守护线程:其他线程都运行结束后,守护线程立即结束
非守护线程:无论其他线程有没有运行结束,本线程都必须正常运行结束后才会结束。
通过 daemon去设置是否为守护线程
语法: Thread().daemon = True /false
线程阻塞
线程阻塞就是指,把当前运行的线程阻塞,让其停止运行,直到所有其他线程运行完毕后,再继续执行。
语法: Thread().join()
那么我们看可以使 用.join()方法,阻塞主线程的运行,让子线程运行完毕之后,再继续运行主线程。
线程锁(Lock)和信号量
线程锁(Lock): 为了防止线程与线程之间资源共享导致的线程安全问题,我们可以对访问的资源加上锁,这个锁就是线程锁。
Lock是锁原语
原语: 一般是指由若干条指令组成的程序段,用来实现某个特定功能,在执行过程中不可被 中断。
Lock对象方法 描述 acquire() 获取锁 release() 释放锁
获取锁后,就可以把当前的资源锁定,其他线程只能等待锁释放后才能获取到该资源。
信号量:信号量是指控制有多少个线程可以访问的相同资源
semaphore=threading.Semaphore(3)#同一时间只能有3个线程处于运行状态
semaphore.acquire() 获取信号量,信号量减一
semaphore.release() 释放信号量,信号量加一
队列
队列:一种数据结构
队列的类型:双向队列(deque)、先进先出队列 、后进先出队列、优先级队列等等
队列的特点:在python中,一切皆对象,所以任何东西都可以放入队列中,使用队列进行管 理。
如类、方法、变量等等。
队列的应用:亿级吞吐量消息队列Kafka;标准消息队列RabbitMq等等;线程池是基于队列 技术实现;
用法:
队列常见操作方法:
from queue import Queue #创建队列
q = Queue(maxsize=10)
Queue.qsize() 返回队列的大小
Queue.empty() 如果队列为空,返回True,反之False
Queue.full() 如果队列满了,返回True,反之False
Queue.full 与 maxsize 大小对应
Queue.get([block[, timeout]])获取队列,timeout等待时间
Queue.get_nowait() 相当Queue.get(False)
Queue.put(item) 写入队列,timeout等待时间
Queue.put_nowait(item) 相当Queue.put(item, False)
Queue.task_done() 在完成一项工作之后,Queue.task_done()函数向任务已经完成的队列
发送一个信号
Queue.join() 实际上意味着等到队列为空,再执行别的操作
线程池
顾名思义就是存放线程的池子,线程池可以控制线程的启动数量,从而达到节省系统资源的 目的。
作用:可以控制要启动的线程数量。 线程池与信号量Semaphore的区别:
线程池控制的是线程数量,而信号量控制的是并发数量。
超过信号量规定数量的线程,已经被启动了,只是状态是挂起。而线程池中,超过了线程池规定数量的线程,还没有启动,只能等待启动。
Python实现线程池的两种方式:
1:concurrent.futures中的ThreadPoolExecutor
2:threadpool库
关于threadpool的使用方式咯!!!
安装threadpool库
1 | pip install threadpool |
导包
1 | from threadpool import ThreadPool, makeRequests |
创建线程池
1 | pool = ThreadPool( 10 ) # 能装下10个线程 |
添加任务
1 | requests = makeRequests(action, args, callback = None ) |
执行任务
1 | [pool.putRequest(req) for req in requests] |
等待执行
1 | pool.wait() |
tomorrow库
多线程库tomorrow是一个专门用来进行并发测试的库,我们使用tomorrow库,可以更容易地实现多线程运行程序
tomorrow是基于concurrent.futures中的ThreadPoolExecutor实现的库,它实现了一个并 发测试的装饰器,通过这个装饰器,我们可以简化代码,达到进行多线程并发测试的目的
1 2 3 4 5 6 7 | from tomorrow import threads import threading @threads ( 10 ) # 最多开启10个线程完成任务 def task( task_name): print ( threading.current_thread().name + "正在:" , task_name) if __name__ = = '__main__' : task_name_list = [ "听歌" , "吃饭" , "聊天" ] for i in range ( 3 ): task(task_name_list[i]) |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· 【自荐】一款简洁、开源的在线白板工具 Drawnix