【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)
重写run方法
复制代码

 

 

执行结果

 

阻塞线程

复制代码
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)
join
复制代码

 

执行结果

 

 

主线程等待所有子线程结束

 

复制代码
# 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

参考:https://zhuanlan.zhihu.com/p/368072959

posted @   Phoenixy  阅读(638)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· DeepSeek在M芯片Mac上本地化部署
· 葡萄城 AI 搜索升级:DeepSeek 加持,客户体验更智能
点击右上角即可分享
微信分享提示