• 并发编程之多线程篇之四


    主要内容:

      一、信号量

      二、Event事件

      三、定时器

      四、线程queue

      五、进程池与线程池

      

    1️⃣ 信号量

      1、信号量的理解

        信号量也是一把锁,可以指定信号量为5,对比互斥锁同一时间只能有一个任务抢到锁去执行,信号量同一时间可以有5个任务拿到锁去执行。

        例如:把互斥锁比作是合租房屋的人去抢一个厕所,那么信号量就是一群路人争抢公共厕所,公共厕所有多个坑位,这意味同一时间可以有多个人争抢公共厕所,公共厕所有多个坑位,这意味着同一时间可以有多个人上公共厕所,但公共厕所容纳的人数是一定的,这便是信号量的大小。

      2、实例:

    #!/usr/bin/env python3
    #-*- coding:utf-8 -*-
    # write by congcong
    from threading import Thread,Semaphore,currentThread
    import time,random
    # Semaphore 指信号量,本质也是互斥锁,不同在于可以同时生成多把锁,把Lock比作私单人卫生间的话,一次只允许一个人使用;那么Semaphere就是公共厕所,一次允许指定的人数同时使用
    
    sm = Semaphore(4) # 生成了五把锁
    def task():
        # sm.acquire()
        # print('%s is in wc'%currentThread().getName())
        # sm.acquire()
        with sm:
            time.sleep(random.randint(1, 3))
            print('%s is in'%currentThread().getName())
    
    if __name__ == '__main__':
        for i in range(10):
            t = Thread(target=task)
            t.start()

    2️⃣ Event事件

      1、Event对象的理解:

        线程的一个关键特性是每个线程都是独立运行且状态不可预测。如果程序中的其 他线程需要通过判断

      某个线程的状态来确定自己下一步的操作,这时线程同步问题就会变得非常棘手。为了解决这些问题,我们需要

      使用threading库中的Event对象。 

         对象包含一个可由线程设置的信号标志,它允许线程等待某些事件的发生。在初始情况下,Event对象中的

      信号标志被设置为假。如果有线程等待一个Event对象, 而这个Event对象的标志为假,那么这个线程将会被一直

      阻塞直至该标志为真。一个线程如果将一个Event对象的信号标志设置为真,它将唤醒所有等待这个Event对象的线程。

      如果一个线程等待一个已经被设置为真的Event对象,那么它将忽略这个事件, 继续执行。

      2、参数介绍:

    from threading import Event
    
    event.isSet():返回event的状态值;
    
    event.wait():如果 event.isSet()==False将阻塞线程;
    
    event.set(): 设置event的状态值为True,所有阻塞池的线程激活进入就绪状态, 等待操作系统调度;
    
    event.clear():恢复event的状态值为False。

      3、实例1:   

    #!/usr/bin/env python3
    #-*- coding:utf-8 -*-
    # write by congcong
    
    from threading import Thread,Event,currentThread
    import time
    
    event = Event()
    def student(name):
        print('学生%s在听课'%name)
        event.wait() # 等待event.set()运行才能释放,接着运行
        print('学生%s下课了'%name)
    
    def teacher(name):
        print('老师%s在上课'%name)
        time.sleep(8)
        event.set() # 释放event.wait(),继续执行
    if __name__ == '__main__':
        s1 = Thread(target=student,args=('cc1',))
        s2 = Thread(target=student,args=('cc2',))
        s3 = Thread(target=student,args=('cc3',))
        t1 = Thread(target=teacher,args=('CC',))
    
        s1.start()
        s2.start()
        s3.start()
        t1.start()
       
    '''
    学生cc1在听课
    学生cc2在听课
    学生cc3在听课
    老师CC在上课
    学生cc2下课了
    学生cc1下课了
    学生cc3下课了
    ''' 

      实例2:

    from threading import Thread,Event,currentThread
    import time
    
    event = Event()
    def client():
        n = 0
        while not event.is_set(): # is_set() 是否激活线程
            if n == 3:
                print('time out,%s connected failed'%currentThread().getName())
                return
            print('%s try %s'%(currentThread().getName(),n))
            event.wait(0.5)
            n += 1
        print('%s is connected'%currentThread().getName())
    
    def check():
        print('%s is checking'%currentThread().getName())
        time.sleep(5)
        event.set()
    if __name__ == '__main__':
        for i in range(3):
            t = Thread(target=client)
            t.start()
        t = Thread(target=check)
        t.start()
    '''
    Thread-1 try 0
    Thread-2 try 0
    Thread-3 try 0
    Thread-4 is checking
    Thread-3 try 1
    Thread-1 try 1
    Thread-2 try 1
    Thread-2 try 2参数
    Thread-1 try 2
    Thread-3 try 2
    time out,Thread-2 connected failed
    time out,Thread-1 connected failed
    time out,Thread-3 connected failed
    '''
    View Code

    3️⃣ 定时器

    1、定时器的理解:

      定时器,即指定n秒后执行某操作。

    2、实例1:

    #!/usr/bin/env python3
    #-*- coding:utf-8 -*-
    # write by congcong
    
    from threading import Thread,Timer
    import random
    def task(name):
        print('hello %s,welcome login!'%name) # hello hyt,welcome login!
    time = Timer(3,task,args=('hyt',)) # 等待指定时间,执行线程
    time.start()

    实例2--自动更新验证码

    #!/usr/bin/env python3
    #-*- coding:utf-8 -*-
    # write by congcong
    
    from threading import Thread,Timer
    import random
    
    class Check_number:
        def __init__(self):
            self.cash_code() # 程序一开始便实例化一个验证码
        def make_code(self,n=4):
            res = ''
            for i in range(n):
                s1 = str(random.randint(0,9)) # 0到9间的任意自然数
                s2 = chr(random.randint(65,90)) # 24个小写字母
                res += random.choice([s1,s2]) # 字符和数字的任意组合
            return res
        def cash_code(self,interval=3):
            self.code = self.make_code()  # 实例化一个验证码
            print(self.code) # 打印验证码
            self.t = Timer(interval,self.make_code) # 定时器,等待指定时间再运行
            self.t.start()
    
        def check(self):
            while True:
                mes = input('输入验证码>>>:').strip()
                if self.code == mes.upper():
                    print('输入正确!')
                    self.t.cancel() # 关闭定时器
                    break
    
    obj = Check_number()
    obj.check()
    View Code

    4️⃣ 线程queue

     1、queue  队列,先进先出

     2、实例: 

    #!/usr/bin/env python3
    #-*- coding:utf-8 -*-
    # write by congcong
    import queue
    # 队列,先进先出
    q = queue.Queue(3) # 括号内指定队列长度
    q.put('one')
    q.put(2)
    q.put('third')
    q.put(4) # 此时队列已满,无法再放数据,造成堵塞,不会提示队列是否已满
    q.put(4,block=True,timeout=5) # 与上式完全相同,但会提示队列已满,block表示阻塞,后面的timeout表示等待,延时指定时间报错
    q.get_nowait(4)  # 表示不等待,直接提示已满
    q.put(4,block=False) # 不阻塞,直接提示,与上式相同
    
    print(q.get()) # one
    print(q.get()) # 2
    print(q.get()) # third
    print(q.get()) # 没有任何数据,且不提示
    print(q.get_nowait()) # 不阻塞,直接报错提示队列已取空
    print(q.get(block=True,timeout=3)) # 阻塞等待3秒,再报错提示
    
    # 堆栈,后进先出,用法和队列相同
    q2 = queue.LifoQueue(3) # 后进先出
    q2.put('one')
    q2.put('two')
    q2.put(3)
    
    print(q2.get())
    print(q2.get())
    print(q2.get())
    
    # 优先级队列
    q3 = queue.PriorityQueue(3)
    q3.put((10,1))
    q3.put((6,2))
    q3.put((20,3))
    print(q3.get())
    print(q3.get())
    print(q3.get())
    '''数字越小优先级越高
    (6, 2)
    (10, 1)
    (20, 3)
    '''

    5️⃣ 进程池与线程池

      1、进程池 

    #!/usr/bin/env python3
    #-*- coding:utf-8 -*-
    # write by congcong
    from concurrent.futures import ProcessPoolExecutor,ThreadPoolExecutor
    import random,os,time
    
    # 进程池
    def task(name):
        print('name:%s pid:%s run '%(name,os.getpid()))
        time.sleep(random.randint(1,5))
    
    if __name__ == '__main__':
        pool = ProcessPoolExecutor(3) # 不指定数目时,默认指定CPU核心数,此处进程池同时接收3个进程,且从始至终只有三个进程在运行(控制进程数)
        for i in range(5):
            pool.submit(task,'cc%s'%i) # 异步提交,不需要等待,提交了就接着提交下一波
        pool.shutdown(wait=True) # 关闭进程池的入口,进程池内进程未执行完之前(进程池内的计数器每执行完一个进程,自减1,直到减为0)不允许其他进程进入进程池
        print('主进程')
    '''未加pool.shutdown()-->wait=True是默认参数
    主进程
    name:cc0 pid:2656 run 
    name:cc1 pid:7316 run 
    name:cc2 pid:4404 run 
    name:cc3 pid:7316 run 
    name:cc4 pid:2656 run 
    '''
    '''加pool.shutdown(wait=True)之后输出,保证了进程的安全性
    name:cc0 pid:1536 run 
    name:cc1 pid:7316 run 
    name:cc2 pid:1900 run 
    name:cc3 pid:7316 run 
    name:cc4 pid:1536 run 
    主进程
    
    '''

      2、线程池

    from threading import Thread,currentThread
    # 线程池
    def task():
        print('name:%s pid:%s is running'%(currentThread().getName(),os.getpid()))
        time.sleep(random.randint(1,3))
    
    if __name__ == '__main__':
        pool = ThreadPoolExecutor(3) # 与进程池同理,此时同时可容纳3个线程,自始至终就3个线程在运行(控制线程数)
        for i in range(5):
            pool.submit(task) # 异步提交,不管是否成功,继续提交下一个
        pool.shutdown() # 关闭线程池入口
        print('主线程')
    '''
    name:ThreadPoolExecutor-0_0 pid:772 is running
    name:ThreadPoolExecutor-0_1 pid:772 is running
    name:ThreadPoolExecutor-0_2 pid:772 is running
    name:ThreadPoolExecutor-0_2 pid:772 is running
    name:ThreadPoolExecutor-0_0 pid:772 is running
    主线程
    '''

      

      

  • 相关阅读:
    找到排序矩阵中从小到大第K个数字
    使用VSCODE开发UE4
    UE4添加模块
    游戏串流
    DIY Arduino 方向盘
    免费/开源软件推荐
    把引擎插件变成工程插件
    MergeActors技巧
    烘焙卡在99%
    UMG里没有"Prefab"怎么办?
  • 原文地址:https://www.cnblogs.com/schut/p/9030051.html
Copyright © 2020-2023  润新知