【Python】threading模块_多线程并发
Python 内置库——threading 多线程
导入
# 导入threading模块 import threading
函数
threading.active_count()
返回当前存活的threading.Thread线程对象数量,等同于len(threading.enumerate())。
threading.current_thread()
返回此函数的调用者控制的threading.Thread线程对象。如果当前调用者控制的线程不是通过threading.Thread创建的,则返回一个功能受限的虚拟线程对象。
threading.get_ident()
返回当前线程的线程标识符。注意当一个线程退出时,它的线程标识符可能会被之后新创建的线程复用。
threading.enumerate()
返回当前存活的threading.Thread线程对象列表。
threading.main_thread()
返回主线程对象,通常情况下,就是程序启动时Python解释器创建的threading._MainThread线程对象。
threading.stack_size([size])
返回创建线程时使用的堆栈大小。也可以使用可选参数size指定之后创建线程时的堆栈大小,size可以是0或者一个不小于32KiB的正整数。
如果参数没有指定,则默认为0。如果系统或者其他原因不支持改变堆栈大小,则会报RuntimeError错误;
如果指定的堆栈大小不合法,则会报ValueError,但并不会修改这个堆栈的大小。
32KiB是保证能解释器运行的最小堆栈大小,当然这个值会因为系统或者其他原因有限制,比如它要求的值是大于32KiB的某个值,只需根据要求修改即可。
threading.currentThread()
返回当前的线程变量
threading.currentThread().name
返回当前的线程的名字
threading.enumerate()
返回一个包含正在运行的线程的list,正在运行指启动后,结束前的线程
threading.activeCount()
返回正在运行的线程数量(与len(threading.enumerate()) 有相同结果)
语法
Tips:如果这个类的初始化方法被重写,请确保在重写的初始化方法中做任何事之前先调用threading.Thread类的__init__方法
threading.Thread(group=None, target=None, name=None, args=(), kwargs={}, *, daemon=None)
参数解析:
group:使用默认值None就好,因为这个参数是为了以后实现ThreadGroup类而保留的。
target:在run方法中调用的可调用对象,即需要开启线程的可调用对象,比如函数或方法。
name:线程名称,默认为“Thread-N”形式的名称,N为较小的十进制数。
args:在参数target中传入的可调用对象的参数元组,默认为空元组()。
kwargs:在参数target中传入的可调用对象的关键字参数字典,默认为空字典{}。
daemon:默认为None,即继承当前调用者线程(即开启线程的线程,一般就是主线程)的守护模式属性,如果不为None,则无论该线程是否为守护模式,都会被设置为“守护模式”。
方法
start()
开启线程活动。它将使得run()方法在一个独立的控制线程中被调用,需要注意的是同一个线程对象的start()方法只能被调用一次,如果调用多次,则会报RuntimeError错误。 run()
此方法代表线程活动。 join(timeout=None)
让当前调用者线程(即开启线程的线程,一般就是主线程)等待,直到线程结束(无论它是什么原因结束的),timeout参数是以秒为单位的浮点数,用于设置操作超时的时间,返回值为None。如果想要判断线程是否超时,只能通过线程的is_alive方法来进行判断。
join方法可以被调用多次。如果对当前线程使用join方法(即线程在内部调用自己的join方法),或者在线程没有开始前使用join方法,都会报RuntimeError错误。 name
线程的名称字符串,并没有什么实际含义,多个线程可以赋予相同的名称,初始值由初始化方法来设置。 ident
线程的标识符,如果线程还没有启动,则为None。ident是一个非零整数,参见threading.get_ident()函数。当线程结束后,它的ident可能被其他新创建的线程复用,当然就算该线程结束了,它的ident依旧是可用的。 is_alive()
线程是否存活,返回True或者False。在线程的run()运行之后直到run()结束,该方法返回True。 daemon
表示该线程是否是守护线程,True或者False。设置一个线程的daemon必须在线程的start()方法之前,否则会报RuntimeError错误。
这个值默认继承自创建它的线程,主线程默认是非守护线程的,所以在主线程中创建的线程默认都是非守护线程的,即daemon=False。
setName():设置线程名
getName():获取线程名
Tips
- 每个线程都有一个唯一标示符,来区分线程中的主次关系
- 线程数量:主线程数 + 子线程数
- 主线程:mainThread,Main函数或者程序主入口,都可以称为主线程。子线程:Thread-x 使用 threading.Thread() 创建出来的都是子线程
- threading.Thread()中daemon 属性默认是 False,这使得 MainThread 需要等待它的结束,自身才结束。所以一般情况下,主线程会等所有的子线程结束之后才会结束。
开启线程
# 创建线程 my_thread = threading.Thread(target=函数名) # 开启线程,当调用start()时,才会真正的创建线程,并且开始执行 my_thread.start()
普通创建线程-通过实例化类创建线程

import time import datetime import threading def times(): times = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S.%f') return times def test_thread(con='HelloWorld', sleep=1): """线程内容""" time.sleep(sleep) print("打印:", con) def main(): # 创建线程 print(f"开始时间:{times()}") thread_default = threading.Thread(target=test_thread) thread_args = threading.Thread(target=test_thread, args=('beybey', 1)) # 启动线程 thread_default.start() thread_args.start() print(f"结束时间:{times()}") if __name__ == '__main__': main()
执行结果
自定义创建线程-继承threading.Thread的子类创建线程

import datetime import threading import time from loguru import logger as logs class playThread(threading.Thread): def __init__(self, env): """重写方法时,确保在所有操作之前先调用threading.Thread.__init__方法""" threading.Thread.__init__(self) self.env =env def run(self): """重写run方法""" tname = self.getName() tid = self.is_alive() logs.info(f"线程内容:{tname},{tid}") case(tname) def singleThread(self, num): """单线程""" for i in range(num): self.run() def manyThread(self, num=2): """多线程""" tList = [] for j in range(num): t = playThread(self.env) t.start() #当调用start()时,才会真正的创建线程,并且开始执行 tList.append(t) logs.info(tList) def case(tname): """测试函数""" for i in range(5): time.sleep(i) logs.info(f"线程{tname}暂停{i}s:{datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S.%f')}") if __name__ == '__main__': '''固定参数''' env = "sit" '''执行''' pt = playThread(env) # pt.singleThread(2) pt.manyThread(5)
执行结果
阻塞线程

import datetime import threading import time from loguru import logger as logs class playThread(threading.Thread): def __init__(self, env, fornum): """重写方法时,确保在所有操作之前先调用threading.Thread.__init__方法""" threading.Thread.__init__(self) self.env = env self.fornum = fornum def run(self): """重写run方法""" tname = self.getName() tid = self.is_alive() logs.info(f"线程内容:{tname},{tid}") case(tname, self.fornum) def singleThread(self, num): """单线程""" for i in range(num): self.run() def manyThread(self, num=2): """多线程""" tList = [] for j in range(num): t = playThread(self.env, self.fornum) t.start() #当调用start()时,才会真正的创建线程,并且开始执行 tList.append(t) logs.warning("调用join阻塞") t.join(1)# 阻塞调用线程(主线程),直到调用join方法的当前结束 logs.info("====end====") def case(tname, fornum): """ 测试函数 :param tname: 线程名称 :param fornum: 循环数 :return: """ for i in range(2): time.sleep(i) logs.info(f"线程{tname}暂停{i}s:{datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S.%f')}") if __name__ == '__main__': '''固定参数''' env = "sit" case_fornum = 2 thread_num = 5 '''执行''' pt = playThread(env=env, fornum=case_fornum) # pt.singleThread(2) pt.manyThread(thread_num)
执行结果
主线程等待所有子线程结束

# coding:utf-8 import threading import time from atexit import register from loguru import logger as logs class Demo(threading.Thread): def __init__(self): """确保在所有操作之前先调用threading.Thread.__init__方法""" threading.Thread.__init__(self) # register(self.delfunc) # def __del__(self): # logs.debug("调用__del__函数") def delfunc(self): logs.debug(f"{self.getName()}调用delfunc") def run(self): logs.debug("run") time.sleep(5) logs.debug("end") def manyThread(self): thread_list = [] for j in range(5): # 判断线程数 t = Demo() thread_list.append(t) # 方法一 t.setDaemon(True) # 设置为守护线程,不会因主线程结束而中断 t.start() # 当调用start()时,才会真正的创建线程,并且开始执行 t.join() # 子线程全部加入,主线程等所有子线程运行完毕 # 方法二 # for t in thread_list: # t.setDaemon(True) # 设置为守护线程,不会因主线程结束而中断 # t.start() # # for t in thread_list: # t.join() # 子线程全部加入,主线程等所有子线程运行完毕 self.delfunc() if __name__ == '__main__': thread = Demo() thread.manyThread()
执行结果:

# coding:utf-8 import threading import time from atexit import register from loguru import logger as logs class Demo(threading.Thread): def __init__(self): """确保在所有操作之前先调用threading.Thread.__init__方法""" threading.Thread.__init__(self) # register(self.delfunc) # def __del__(self): # logs.debug("调用__del__函数") def delfunc(self): logs.debug(f"{self.getName()}调用delfunc") def run(self): logs.debug("run") time.sleep(5) logs.debug("end") def manyThread(self): thread_list = [] for j in range(5): # 判断线程数 t = Demo() thread_list.append(t) # # 方法一 # t.setDaemon(True) # 设置为守护线程,不会因主线程结束而中断 # t.start() # 当调用start()时,才会真正的创建线程,并且开始执行 # t.join() # 子线程全部加入,主线程等所有子线程运行完毕 # 方法二 for t in thread_list: t.setDaemon(True) # 设置为守护线程,不会因主线程结束而中断 t.start() for t in thread_list: t.join() # 子线程全部加入,主线程等所有子线程运行完毕 self.delfunc() if __name__ == '__main__': thread = Demo() thread.manyThread()
执行结果:
拓展
线程和进程的区别
主线程:main方法 子线程:非主线程皆是子线程 守护线程;在进程中,为主线程提供一种通用服务的线程 非守护线程(即用户线程):通常异步处理一些业务或逻辑 <Tips:用户线程在start之前可以通过setDaemo(true)来转变为守护线程。> 进程和线程的区别 线程是被系统独立调度和分派的基本单位,线程自己不拥有系统资源,只拥有一点儿在运行中必不可少的资源,但它可与同属一个进程的其它线程共享进程所拥有的全部资源 主进程运行:主进程代码运行完毕 主线程运行:所在的进程内的所有非守护线程运行完毕后,主线程才算运行完毕 守护进程:主进程要等非守护进程都运行完毕后再回收子进程的资源才结束,守护的是主进程 守护线程:主线程在其他非守护线程运行完毕后才算结束,守护的是非守护线程 主线程的结束意味着进程结束,进程整体的资源都会被回收,而进程必须保证非守护线程都运行完毕后才能结束
参考:https://blog.csdn.net/hahahzzzzz/article/details/115045179
-------------------------------------------------------------------------------------
如果万事开头难 那请结局一定圆满 @ Phoenixy
-------------------------------------------------------------------------------------
分类:
Python
标签:
python 模块及工具
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· DeepSeek在M芯片Mac上本地化部署
· 葡萄城 AI 搜索升级:DeepSeek 加持,客户体验更智能