• 【Python】python多线程


    1. 进程与线程的概念

    1.1 进程

    进程:进程是操作系统进行资源分配和调度运行的基本单位,一个正在运行的程序就是一个进程。

    查看进程的两种方法:

    • 打开Windows任务管理器
    • 打开cmd,输入命令tasklist

    1.打开Windows任务管理器如下:Google Chrome这个应用,后面有数字(24),代表这个应用正在运行24个进程。

    在这里插入图片描述

    2.打开cmd,输入命令tasklist如下:

    在这里插入图片描述

    1.2 线程

    线程:线程是程序执行的最小单元,进程负责分配资源,而线程是使用分配的资源去执行程序。

    • 进程是线程的容器,一个进程下最少有一个线程。
    • 一个进程下的所有线程,共享该进程的所有资源。

    2. threading模块

    1.threading模块的方法:

    方法描述
    threading.currentThread()返回当前的线程变量
    threading.enumerate()返回一个包含正在运行的线程列表
    threading.activeCount()返回正在运行的线程数量

    2.threading模块的Thread类的方法:

    方法描述
    run()用以表示线程活动的方法
    start()启动线程活动
    join()等待至线程中止
    isAlive()返回线程是否活动的
    getName()返回线程名
    setName()设置线程名

    3.threading模块的Lock类的方法:

    方法描述
    acquire()获得锁
    release()释放锁

    2.1 threading模块的方法

    2.2 多线程的创建与使用

    1.通过threading.Thread()创建对象,并使用多线程。

    import threading
    
    def test1():
        for i in range(1,6):
            print(i)
    
    def test2(x,y):
        for i in range(x,y):
            print(i)
    
    if __name__=="__main__":
        t1 = threading.Thread(name='thread1', target=test1) # name的值为线程名,target的值为函数名
        t2 = threading.Thread(name='thread2', target=test2, args=(11,16)) # args的值为函数的传参
        t1.start() # 启动线程thread1
        t2.start() # 启动线程thread2
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    运行结果:

    1
    2 
    11
    12
    3 
    4 
    13
    14
    15
    5 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    1.从运行结果可以看出,多线程是并发的
    2.多运行几次程序,会发现每次运行结果不一致

    2.通过继承threading.Thread类,然后重写__init__()方法和run()方法,再创建对象,并使用多线程。

    import threading
    
    class myThread(threading.Thread):
        def __init__(self, name, target):
            threading.Thread.__init__(self)
            self.name = name
            self.target = target
            
        def run(self):
            print("启动线程: {}".format(self.name))
            self.target()
            
    def test1():
        for i in range(1,6):
            print(i)
    
    def test2():
        for i in range(11,16):
            print(i)
    
    if __name__=="__main__":
        t1 = myThread(name='thread1', target= test1)
        t2 = myThread(name='thread2', target= test2)
        t1.start()
        t2.start()
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    运行结果:

    启动线程: thread1
    1
    启动线程: thread2
    11
    12
    13
    14
    2
    15
    3
    4
    5
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    2.3 主线程与守护线程

    主线程:主线程会等待所有的子线程运行结束后,再结束。
    守护线程:当设置一个线程为守护线程后,随着主线程的结束,守护线程也会跟着结束。

    例1:一个主线程,两个子线程。

    • 设置子线程1为守护线程;
    • 主线程先后调用子线程1和子线程2;
    • 构造子线程1的运行时间大于子线程2;
    • 主线程等待两个子线程运行,子线程2先运行结束,子线程1本来也还在运行,但由于主线程和子线程2都结束了,作为守护线程的子线程1也跟着结束。
    import threading
    import time
    
    def test1():
        print("Hello World")
        time.sleep(2)
        print("Hello World")
    
    def test2():
        for i in range(1,6):
            print(i)
    
    if __name__=="__main__":
        t1 = threading.Thread(name='thread1', target=test1) 
        t2 = threading.Thread(name='thread2', target=test2) 
        t1.daemon = True # 设置守护线程
        t1.start()
        t2.start()
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    运行结果:

    Hello World
    1
    2
    3
    4
    5
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    1.注意:设置守护线程要在放在线程启动之前,即t.daemon = True要放在t.start()之前。
    2.看结果可以发现,本来子线程thread1要打印两次Hello World,但是因为主线程和子线程thread2都运行结束了,thread1作为守护线程,也跟着结束了,所以第二次Hello World没有打印。

    例2:一个主线程,两个子线程。

    • 子线程1无限循环,设置子线程1为守护线程;
    • 子线程2运行时间有限;
    • 相当于子线程2运行结束,子线程1就跳出了无限循环。
    import threading
    import time
    from datetime import datetime
    
    def test1():
        while True:
            print(datetime.now())
            time.sleep(0.5)
    
    def test2():
        for i in range(1,6):
            print(i)
            time.sleep(0.5)
    
    if __name__=="__main__":
        t1 = threading.Thread(name='thread1', target=test1) 
        t2 = threading.Thread(name='thread2', target=test2) 
        t1.daemon = True # 设置守护线程
        t1.start()
        t2.start()
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    运行结果:

    2022-11-04 22:38:41.222094
    1
    2022-11-04 22:38:41.733547
    2
    3
    2022-11-04 22:38:42.241197
    2022-11-04 22:38:42.751068
    4
    2022-11-04 22:38:43.253057
    5
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    2.4 线程阻塞

    使用join()可以实现线程阻塞,从而控制线程运行的先后顺序。

    注:

    1. join()不设置时间,则等待线程结束后,再执行后续代码。
    2. join(3)设置时间3秒,则等待线程执行3秒后,再执行后续代码。

    1.等待执行线程1结束后,再执行主线程和线程2。

    import threading
    
    def test1():
        for i in range(1,6):
            print(i)
    
    def test2(x,y):
        for i in range(x,y):
            print(i)
    
    if __name__=="__main__":
        t1 = threading.Thread(name='thread1', target=test1) 
        t2 = threading.Thread(name='thread2', target=test2, args=(11,16)) 
        t1.start() 
        t1.join() # 等待线程thread1结束后,再执行后续代码
        t2.start()   
        print("hello world")
        
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    运行结果:

    1
    2
    3
    4
    5
    11
    hello world
    12
    13
    14
    15
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    2.等待执行线程1结束后,再等待执行线程2结束,最后执行主线程。

    import threading
    
    def test1():
        for i in range(1,6):
            print(i)
    
    def test2(x,y):
        for i in range(x,y):
            print(i)
    
    if __name__=="__main__":
        t1 = threading.Thread(name='thread1', target=test1) 
        t2 = threading.Thread(name='thread2', target=test2, args=(11,16)) 
        t1.start() 
        t1.join() # 等待线程thread1结束后,再执行后续代码
        t2.start()  
        t2.join() # 等待线程thread2结束后,再执行后续代码
        print("hello world")
        
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    运行结果:

    1
    2
    3
    4
    5
    11
    12
    13
    14
    15
    hello world
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    3.等待执行线程1运行3秒后,再执行主线程和线程2。

    import threading
    import time
    
    def test1():
        for i in range(1,6):
            print(i)
            time.sleep(1)
    
    def test2(x,y):
        for i in range(x,y):
            print(i)
    
    if __name__=="__main__":
        t1 = threading.Thread(name='thread1', target=test1) 
        t2 = threading.Thread(name='thread2', target=test2, args=(11,16)) 
        t1.start() 
        t1.join(3) # 等待线程thread1运行3秒后,再执行后续代码
        t2.start()  
        print("hello world")
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    运行结果:

    1
    2
    3
    11
    hello world
    12
    13
    14
    15
    4
    5
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    2.5 线程同步

    多个线程同时运行,由于多线程是并发的,各个线程之间会互相抢占计算资源,导致程序混乱。
    如果多个线程共同对某个数据修改,可能会出现数据不同步的问题,为了保证数据的正确性,需要进行多线程同步。

    使用threading.Lock(),可以对线程进行上锁和解锁,从而实现程同步,其中:

    • acquire()方法用于给线程上锁
    • release()方法用于给线程解锁

    1.未对线程上锁,并对变量number进行修改,运行后发现很混乱,不符合预期结果。

    import threading
    
    number = 0
    
    def update_number():
        global number 
        print("old number:{}".format(number))  
        number += 10
        print("new number:{}".format(number))
        print("---------------")
    
    
    if __name__=="__main__":
        # 创建5个线程,添加进列表
        threadList = []
        for i in range(5):
            t = threading.Thread(target=update_number) 
            threadList.append(t) 
        # 依次启动5个线程
        for t in threadList:
            t.start() 
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    运行结果:

    old number:0
    new number:10
    old number:10
    new number:20
    ---------------
    ---------------
    old number:10
    new number:30
    ---------------
    old number:20
    new number:40
    ---------------
    old number:40
    new number:50
    ---------------
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    2.对线程上锁,并对变量number进行修改,运行后符合预期结果。

    import threading
    
    number = 0
    threadLock = threading.Lock() # 创建Lock对象
    
    def update_number():
        threadLock.acquire() # 获得锁
        global number 
        print("old number:{}".format(number))  
        number += 10
        print("new number:{}".format(number))
        print("---------------")
        threadLock.release() # 释放锁
    
    if __name__=="__main__":
        # 创建5个线程,添加进列表
        threadList = []
        for i in range(5):
            t = threading.Thread(target=update_number) 
            threadList.append(t) 
        # 依次启动5个线程
        for t in threadList:
            t.start() 
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    运行结果:

    old number:0
    new number:10
    ---------------
    old number:10
    new number:20
    ---------------
    old number:20
    new number:30
    ---------------
    old number:30
    new number:40
    ---------------
    old number:40
    new number:50
    ---------------
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
  • 相关阅读:
    如果你有一次自驾游的机会,你会如何准备?
    清洁服务机器人---洗地机SOC SSD222开发经验总结
    基于git+gitlab+jenkins实现python项目自动化发布
    2022年找工作确实不易
    ansible自动化部署web服务
    maptalks--热力分析、聚合分析
    Multichain跨链无法到账,DApp真去中心化or伪去中心化?
    【优选算法】—— 前缀和算法
    jvm调优 和实际案例
    CEF 桌面软件开发实战
  • 原文地址:https://blog.csdn.net/aidijava/article/details/127624072