zoukankan      html  css  js  c++  java
  • python 多线程、线程锁、事件

    1. 多线程的基本使用

    import threading
    import time
    
    def run(num):
        print('Num: %s'% num)
        time.sleep(3)
    
        if num == 4:
            print('Thread is finished.')
    # 对函数 run 创建5个线程
    for i in range(5): # 创建线程,target:目标函数,args:函数的参数,使用元组的形式 t = threading.Thread(target=run,args=(i,)) # 开始线程活动 t.start() time.sleep(0.01) print('Main thread is finished.')
    结果:
    Num: 0 Num:
    1 Num: 2 Num: 3 Num: 4 Main thread is finished. Thread is finished.

    # 上面打印的顺序是先打印
    Main thread is finished.因为主线程已经完成了,而子线程里 slee(3),所以此时子线程尚未完成,大约3秒后,才打印的 Thread is finished.
    
    

    PS. 线程类的使用

    import threading
    import time
    
    num = 0
    lock = threading.Lock()  # 互斥锁,因为多个线程要对共享的数据:num 进行修改,防止出现错误。
    
    class MyThread(threading.Thread):  # 继承线程类
        def __init__(self):
            super().__init__() # 写了__init__,就一定要继承父类的构造方法。父类的__init__的参数是默认参数,故可以不用重写父类的参数。
    
        def task(self):  # 自定义的函数
            global num
            if lock.acquire():  # 如果上锁成功,则执行:
                num+=1
                print(f'{self.name}, Num is : {num}')  # 打印线程名和num的值
                lock.release()   # 释放锁,让数据供其他线程使用
                time.sleep(1)
    
    
        def task2(self):
            time.sleep(0.1)
            print(f'{self.name}','Task2 is being called.')
            time.sleep(1)
    
        def run(self):  # 重写父类的run方法。每个单独的线程控制中,start()会自动调用 run()方法。
            self.task()
            self.task2()
    
    
    for i in range(10):
        T = MyThread() # 针对每个线程对象,start()最多只能调用一次,否则会抛出错误。所以才每循环一次,在循环内部重新生成一个对象
        T.start()

    2. 等待子线程执行:join

    import threading
    import time
    
    def run(num):
        print('Num: %s'% num)
        time.sleep(3)
    
        if num == 4:
            print('Thread is finished.')
    
    Tlist = []
    for i in range(5):
        # 创建线程,target:目标函数,args:函数的参数,使用元组的形式
        t = threading.Thread(target=run,args=(i,))
        # 开始线程活动
        t.start()
        Tlist.append(t)
    
    for t in Tlist:  # 针对每个子线程,等待子线程执行一段时间,再继续往下执行主线程。
        # t.join(0.1)  # 针对每个子线程,等待0.1秒,继续执行主线程
        t.join()   # 没写时间,则默认等待子线程执行完毕,才继续往下执行主线程。
    
    time.sleep(0.01)
    print('Main thread is finished.')
    结果:
    Num: 0
    Num: 1
    Num: 2
    Num: 3
    Num: 4
    Thread is finished.
    Main thread is finished.

    # 子线程使用了join方法,等待子线程执行完毕才继续执行主线程,所以打印顺序是正常的。

    3. 守护线程

    即只要主线程执行完毕,不管子线程状态如何,都会被强制杀掉。

    import threading
    import time
    
    def run(num):
        print('Num: %s'% num)
        time.sleep(3)
    
        if num == 4:
            print('Thread is finished.')
    
    Tlist = []
    for i in range(5):
        # 创建线程,target:目标函数,args:函数的参数,使用元组的形式
        t = threading.Thread(target=run,args=(i,))
        # 设置为守护线程
        t.setDaemon(True)
        # 开始线程活动
        t.start()
        Tlist.append(t)
    
    for t in Tlist:  # 针对每个子线程,等待子线程执行一段时间,再继续往下执行主线程。
        t.join(0.1)  # 对子线程等待0.1秒后,继续执行主线程
        # t.join()   # 没写时间,则默认等待子线程执行完毕,才继续往下执行主线程。
    
    time.sleep(0.01)
    print('Main thread is finished.')
    print('Current Thread:',threading.activeCount()) # 打印当前活动线程数量,这是主线程最后一个语句,所以不管此时还有多少个活动的守护线程,当此句执行完毕,守护线程都会被杀掉。
    结果:
    Num: 0 Num:
    1 Num: 2 Num: 3 Num: 4 Main thread is finished. Current Thread: 6

    # 少打印了 Thread is finished. 因为 主线程结束后,子线程就被杀掉了。

    4. 线程锁

    GIL锁:  Global Interpreter Lock,全局解释器锁。为了解决多线程之间数据完整性和状态同步的问题,在任意时刻只有一个线程在解释器中运行。这是CPython中的问题。

    线程安全:在多线程中,共享的数据同一时间只能由一个线程执行。

    互斥锁:(threading.Lock) 并不关心当前是哪个线程占有了该锁;如果该锁已经被占有了,那么任何其它尝试获取该锁的线程都会被阻塞,包括已经占有该锁的线程也会被阻塞。

    递归锁:threading.RLock。RLock内部维护着一个Lock和一个counter变量,counter记录了acquire的次数,从而使得资源可以被多次require。直到一个线程所有的acquire都被release,其他的线程才能获得资源(已经占用该锁的线程,可以多次actuire,并不会阻塞)

    死锁:死锁就是当一个线程已经占用了锁并且尚未释放,但是又有新的获取该锁的请求,亦或着两个线程分别占据着对方想要获取的锁,此时线程进入了阻塞的状态,就是死锁。

    部份转自:https://www.jb51.net/article/74426.htm

    部份转自:https://www.cnblogs.com/ArsenalfanInECNU/p/10022740.html

    e.g.

    互斥锁:threading.Lock

    import threading
    
    class MyThread(threading.Thread):
        def run(self):  # 这是重写的父类的run方法
            global num
            if mutex.acquire():  # 判断能否加锁,acquire()返回的是加锁的状态,加锁成功就是True
                num = num+1
                msg = self.name+' set num to '+str(num)
                print(msg)
                # mutex.acquire()   # 这两句话如果不注释掉,就会进入死锁的状态。因为上一个锁还未释放,再次请求加锁,就会阻塞。
                # mutex.release()
                mutex.release()  # 释放锁
    num = 0
    mutex = threading.Lock()
    def test():
        for i in range(5):
            t = MyThread()
            t.start()
    if __name__ == '__main__':
        test()

    普通方法:

    import threading
    import time
    
    def run(n):
        global num
        if mutex.acquire(): # 返回获取锁的结果,加锁成功为True,失败false
            num = num+1
            msg ='Thread-%s set num to ' %n + str(num)
            print(msg)
            mutex.release()
            time.sleep(2)
    
    num = 0
    mutex = threading.Lock()
    
    for i in range(5):
        t = threading.Thread(target=run,args=(i+1,))
        t.start()

    递归锁:threading.RLock

    import threading
    
    class MyThread(threading.Thread):
        def run(self):
            global num
            if mutex.acquire():
                num = num+1
                msg = self.name+' set num to '+str(num)
                print(msg)
                mutex.acquire()  # 虽然再次acquire,但是并不会阻塞
                mutex.release()
                mutex.release()
    num = 0
    mutex = threading.RLock()
    def test():
        for i in range(5):
            t = MyThread()
            t.start()
    if __name__ == '__main__':
        test()

     5. 信号量 semaphore

    信号量用于限制同时运行的线程数量

    import threading
    import time
    
    def run(n):
        sema.acquire()
        print('Thread: %s' %n)
        time.sleep(2)
        sema.release()
    # 声明信号量,同时允许5个线程执行 sema
    = threading.BoundedSemaphore(5) for i in range(20): t = threading.Thread(target=run,args=(i+1,)) t.start()

     6. 事件:Event

    事件可以用于多个线程进行简单通讯,事件可以控制一个内部 flag,初始时是 False,通过 set 方法使其变成 True,clear 方法使其变成 False,flag 为 False 时 wait 方法会一直阻塞直到flag 为True。

    import threading
    import time
    
    # 事件可以控制一个内部标志,通过set方法使其变成True,clear方法使其变成False,为False时wait方法会一直阻塞直到标志为True。
    # 这个标志初始时是False
    
    event = threading.Event()
    # event.set()    # 设置flag为True
    # event.clear()  # 设置flag为False
    # event.wait()   # 一直等待(阻塞),直到有线程使用 set 方法将 flag 变成 True
    # event.is_set() # 返回flag的值
    
    def boss():
        print('Boss: 今天加班5秒钟')
        event.clear()   # 清除 flag,使其值变为 false
        time.sleep(5)
        print("Boss: 时间到了,大家回家吧")
        event.set()     # 设置 flag,使其值变为 True
    
    
    def employee():
        while True:
            if event.is_set():     # 判断:如果 flag 为 True
                print('Employee: 回家了!')
                break
            else:
                print('Emplyee: 工作中,请勿打扰...')
                event.wait()      # 如果 flag 为 False,则使用 wait 方法一直等待,直到有线程将其设置为 True
    
    
    # 创建两个线程,分别为boss和employee,让它们根据 event 进行简单的通讯 b
    = threading.Thread(target=boss,) e = threading.Thread(target=employee,) b.start() e.start()
    结果:
    Boss: 今天加班5秒钟 Emplyee: 工作中,请勿打扰... Boss: 时间到了,大家回家吧 Employee: 回家了!
  • 相关阅读:
    C++小知识之Vector用法
    云计算和大数据入门
    C++解析JSON之JsonCPP
    OSS研究
    linux内核--进程地址空间(三)
    学习笔记:修改网吧计费系统
    学习笔记:找回电脑开机密码
    例说C#深拷贝与浅拷贝
    JBossESB教程(一)——开发环境的搭建
    Java集合---ConcurrentHashMap原理分析
  • 原文地址:https://www.cnblogs.com/wztshine/p/11965933.html
Copyright © 2011-2022 走看看