zoukankan      html  css  js  c++  java
  • (并发编程线程)

        进程部分:
            1. 进程间通信=>IPC
            2. 生产者消费者模型(*****)
    
        线程部分:
            1. 线程理论(*****)
            2. 开启线程的两种方式(*****)
            3. 线程对象其他相关的属性或方法
            4. 守护线程
            5. 线程互斥锁
            6. GIL全局解释器锁(******)
            7. 死锁与递归锁
    1.进程间通信
    进程之间通信必须找到一种介质,该介质必须满足
    1、是所有进程共享的
    2、必须是内存空间
    附加:帮我们自动处理好锁的问题
    
    a、from multiprocessing import Manager(共享内存,但要自己解决锁的问题)
    b、IPC中的队列(Queue) 共享,内存,自动处理锁的问题(最常用)
    c、IPC中的管道(Pipe),共享,内存,需自己解决锁的问题
    
    a、用Manager
    from multiprocessing import Process,Manager,Lock
    import time
    
    mutex=Lock()
    
    def task(dic,lock):
        lock.acquire()
        temp=dic['num']
        time.sleep(0.1)
        dic['num']=temp-1
        lock.release()
    
    if __name__ == '__main__':
        m=Manager()
        dic=m.dict({'num':10})
    
        l=[]
        for i in range(10):
            p=Process(target=task,args=(dic,mutex))
            l.append(p)
            p.start()
    
        for p in l:
            p.join()
    print(dic)
    b、用队列Queue
    1)共享的空间
    2)是内存空间
    3)自动帮我们处理好锁定问题
    
    from multiprocessing import Queue
    q=Queue(3)  #设置队列中maxsize个数为三
    q.put('first')
    q.put({'second':None})
    q.put('')
    # q.put(4)   #阻塞。不报错,程序卡在原地等待队列中清出一个值。默认blok=True
    print(q.get())
    print(q.get())
    print(q.get())
    
    强调:
    1、队列用来存成进程之间沟通的消息,数据量不应该过大
    2、maxsize的值超过的内存限制就变得毫无意义
                                                                  
                                                                                                                                                                                                          
    了解:
    q=Queue(3)
    q.put('first',block=False)
    q.put('second',block=False)
    q.put('third',block=False)
    q.put('fourth',block=False)  #报错 queue.Full
    
    q.put('first',block=True)
    q.put('second',block=True)
    q.put('third',block=True)
    q.put('fourth',block=True,timeout=3)  #等待3秒后若还进不去报错。注意timeout不能和block=False连用
    
    q.get(block=False)
    q.get(block=False)
    q.get(block=False)
    q.get(block=False)           #报错 queue.Empty
    
    q.get(block=True)
    q.get(block=True)
    q.get(block=True)
    q.get(block=True,timeout=2)    #等待2秒后还取不出东西则报错。注意timeout不能和block=False连用
    2.生产者消费者模型
    该模型中包含两类重要的角色:
    1、生产者:将负责造数据的任务比喻为生产者
    2、消费者:接收生产者造出的数据来做进一步的处理,该类人物被比喻成消费者
    
    实现生产者消费者模型三要素
    1、生产者
    2、消费者
    3、队列
    
    什么时候用该模型:
    程序中出现明显的两类任何,一类任务是负责生产,另外一类任务是负责处理生产的数据的
    
    该模型的好处:
    1、实现了生产者与消费者解耦和
    2、平衡了生产者的生产力与消费者的处理数据的能力
    
    
    import time
    import random
    from multiprocessing import Process,Queue
    
    def consumer(name,q):
        while True:
            res=q.get()
            time.sleep(random.randint(1,3))
            print('33[46m消费者===》%s 吃了 %s33[0m' %(name,res))
    
    def producer(name,q,food):
        for i in range(5):
            time.sleep(random.randint(1,2))
            res='%s%s' %(food,i)
            q.put(res)
            print('33[45m生产者者===》%s 生产了 %s33[0m' %(name,res))
    
    if __name__ == '__main__':
        #1、共享的盆
        q=Queue()
    
        #2、生产者们
        p1=Process(target=producer,args=('egon',q,'包子'))
        p2=Process(target=producer,args=('刘清政',q,'泔水'))
        p3=Process(target=producer,args=('杨军',q,'米饭'))
    
        #3、消费者们
        c1=Process(target=consumer,args=('alex',q))
        c2=Process(target=consumer,args=('梁书东',q))
    
        p1.start()
        p2.start()
        p3.start()
        c1.start()
    c2.start()
    
    生产者消费者模型是解决问题的思路不是技术。可以用进程和队列来实现,也可以用其他的来实现。
    
    
    并发编程之线程
    多任务可以由多进程完成,也可以由一个进程内的多线程完成。
    我们前面提到了进程是由若干线程组成的,一个进程至少有一个线程。
    由于线程是操作系统直接支持的执行单元,因此,高级语言通常都内置多线程的支持,Python也不例外,并且,Python的线程是真正的Posix Thread,而不是模拟出来的线程。
    Python的标准库提供了两个模块:_thread和threading,_thread是低级模块,threading是高级模块,对_thread进行了封装。绝大多数情况下,我们只需要使用threading这个高级模块。
    启动一个线程就是把一个函数传入并创建Thread实例,然后调用start()开始执行:
    一、线程理论
    1、什么是线程
       线程指的是一条流水线的工作过程
       进程不是执行单位,是资源单位
       一个进程内自带一个线程,线程是执行单位
    
    2、进程VS线程
        1、同一进程内的线程们共享该进程内资源,不同进程内的线程资源肯定是隔离的
    2、创建线程的开销比创建进程要小的多
    
    3、线程中没有父子关系。相较于子线程、主线程特殊之处在于其代变了主进程的生命周期。
    
    主进程等待子进程结束然后结束,是为子进程回收资源。
    主线程等待子线程结束然后结束,是等待这个进程的代码(其他非守护线程)执行完毕。
    
    主进程:执行完代码就结束。
    主线程:所以子线程结束才结束。
    
    二、开启线程的两种方式
    方式一:导入Thread模块
    from threading import Thread
    import time
    
    def task(name):
        print('%s is running' %name)
        time.sleep(3)
    
    if __name__ == '__main__':
        t=Thread(target=task,args=('egon',))
        t.start()
    print('主线程')
    
    方式二:创建类继承Thread
    from threading import Thread
    import time
    
    class MyThread(Thread):
        def run(self):
            print('%s is running' %self.name)
            time.sleep(3)
    
    if __name__ == '__main__':
        t=MyThread()
        t.start()
    print('主线程')
    三、进程vs线程
    1、瞅一瞅PID (Process ID)
    from threading import Thread
    import time,os
    
    def task():
        print('%s is running' %os.getpid())
        time.sleep(3)
    
    if __name__ == '__main__':
        t=Thread(target=task,)
        t.start()
        print('主线程',os.getpid()) #一个进程中的子线程pid相同
    
    2、线程创建开销小
    
    3、同一进程内的多个线程共享该进程内的资源
    from threading import Thread
    import time,os
    
    x=1000
    def task():
        global x
        x=0
    
    if __name__ == '__main__':
        t=Thread(target=task,)
        t.start()
        t.join()
        print('主线程',x)   #主线程 0
    
    四、线程对象的其他方法
    from threading import Thread,current_thread,active_count,enumerate
    import time,os
    
    def task():
        print('%s is running' %current_thread().name)  #Thread-1 is running
        time.sleep(3)
    
    if __name__ == '__main__':
        t1=Thread(target=task,name='第一个线程')
        t2=Thread(target=task,)
        t3=Thread(target=task,)
        t1.start()
        t2.start()
        t3.start()
        print(t1.is_alive())  #True
        print(active_count()) #4
        print(enumerate())    #[<_MainThread(MainThread, started 4320768832)>, <Thread(第一个线程, started 123145551912960)>, <Thread(Thread-1, started 123145557168128)>, <Thread(Thread-2, started 123145562423296)>]  #当前活跃的线程
    print('主线程',current_thread().name)  #主线程 MainThread
    
    五、守护线程
    from threading import Thread,current_thread
    import time
    
    def task():
        print('%s is running' %current_thread().name)
        time.sleep(3)
    
    if __name__ == '__main__':
        t1=Thread(target=task,name='第一个线程')
        t1.daemon = True
        t1.start()
    
    print('主线程')
    
    from threading import Thread
    import time
    def foo():
        print(123)
        time.sleep(5)
        print("end123")
    
    def bar():
        print(456)
        time.sleep(3)
        print("end456")
    
    if __name__ == '__main__':
    
        t1=Thread(target=foo)
        t2=Thread(target=bar)
    
        t1.daemon=True
        t1.start()
        t2.start()
        print("main-------")
    
        '''
        123
        456
        main-------
        end456
    '''
    
    主进程:执行完代码就结束。
    主线程:所以子线程结束才结束。
    总结:只要进程内没有可执行的代码守护就结束
    六、线程互斥锁
    from threading import Thread,Lock
    import time
    
    mutex=Lock()
    x=100
    
    def task():
        global x
        mutex.acquire()
        temp=x
        time.sleep(0.1)
        x=temp-1
        mutex.release()
    
    if __name__ == '__main__':
        start=time.time()
        t_l=[]
        for i in range(100):
            t=Thread(target=task)
            t_l.append(t)
            t.start()
        for t in t_l:
            t.join()
    
        print('',x)    #0
    print(time.time()-start)
  • 相关阅读:
    Java 8与Runtime.getRuntime().availableProcessors()
    nginx配置
    周末完成工作小结
    CentOS 8 安装MySQL 8.0
    centOS8网络获取不了
    IUAP平台新增菜单存储过程
    centOS8安装Docker
    Mybatis里用到的设计模式
    2020,回顾过往,展望未来
    使用 Apache SSI(Server Side Includes) 制作多语言版静态网页
  • 原文地址:https://www.cnblogs.com/zhaijihai/p/9598758.html
Copyright © 2011-2022 走看看