zoukankan      html  css  js  c++  java
  • 并发编程之多线程篇之四

    主要内容:

      一、信号量

      二、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
    主线程
    '''

      

      

  • 相关阅读:
    POJ 2251 Dungeon Master
    HDU 3085 Nightmare Ⅱ
    CodeForces 1060 B Maximum Sum of Digits
    HDU 1166 敌兵布阵(树状数组)
    HDOJ 2050 折线分割平面
    HDU 5879 Cure
    HDU 1878 欧拉回路
    HDU 6225 Little Boxes
    ZOJ 2971 Give Me the Number
    HDU 2680 Choose the best route
  • 原文地址:https://www.cnblogs.com/schut/p/9030051.html
Copyright © 2011-2022 走看看