• 并发编程中死锁、递归锁、进程/线程池、协程TCP服务器并发等知识点


    1、死锁

    定义; 类似两个人分别被囚禁在两间房子里,A手上拿着的是B囚禁房间的钥匙,而B拿着A的钥匙,两个人都没法出去,没法给对方开锁,进而造成死锁现象。
    具体例子代码如下:

    # -*-coding:utf-8 -*-
    from threading import Thread,Lock,RLock
    import time
    muxeA=Lock()
    muxeB=Lock()
    
    class  MyThread(Thread):
        def run(self):
            self.func1()
            self.func2()
    
        def func1(self):
            muxeA.acquire()
            print('%s抢到A锁了'%self.name)
            muxeB.acquire()
            print('%s抢到B锁了' % self.name)
            muxeB.release()
            muxeA.release()
    
        def func2(self):
            muxeB.acquire()
            print('%s抢到B锁了' % self.name)
            time.sleep(2)
            muxeA.acquire()
            print('%s抢到A锁了' % self.name)
            muxeA.release()
            muxeB.release()
    
    if __name__ == '__main__':
        for i in range(10):
            t=MyThread()
            t.start()
    分析:
    当执行func1的时候,第一个人(a)先抢到锁A,这时候,
    其他人只能继续等待抢锁A,没有人跟a抢锁B,所以a也抢到锁B,
    拿到锁B后,a就把锁B先释放掉,再释放锁A,
    继续执行func2,a也顺利抢到锁B,这时候,睡眠2s,
    而锁A已经被第二个人抢到了,这时候,第二个人只拿到锁A,没有拿到锁B,
    所以没有释放锁A,而a一直在等待抢锁A,没有释放锁B,
    所以这时候就造成等待死循环的情况。

    执行结果如下:

    在这里插入图片描述

    2、递归锁:

    递归锁使用:from reading import RLock

    递归锁的特点:
    1、可以被连续的acquire和release
    2、但是,只能第一个抢到这把锁执行上述操作
    3、它内部有一个计数器,每acquire一次计数加一,每realse一次计数减一
    4、只要计数不为0,那么其他人都无法抢到该锁

    具体例子代码如下:

    # -*-coding:utf-8 -*-
    from threading import Thread,RLock
    import time
    
    muxeA=muxeB=RLock()
    
    class  MyThread(Thread):
        def run(self):
            self.func1()
            self.func2()
    
        def func1(self):
            muxeA.acquire()
            print('%s抢到A锁了'%self.name)
            muxeB.acquire()
            print('%s抢到B锁了' % self.name)
            muxeB.release()
            muxeA.release()
    
        def func2(self):
            muxeB.acquire()
            print('%s抢到B锁了' % self.name)
            time.sleep(2)
            muxeA.acquire()
            print('%s抢到A锁了' % self.name)
            muxeA.release()
            muxeB.release()
    
    if __name__ == '__main__':
        for i in range(10):
            t=MyThread()
            t.start()

    执行结果如下(没有出现死锁现象):
    在这里插入图片描述

    3、信号量:
    定义:就相当于多个互斥锁:
    具体例子如下:

    from threading import Thread,Semaphore
    import time,random
    s=Semaphore(5)
    
    def task(name):
        s.acquire()
        print('%s号停车位正在停车'%name)
        time.sleep(random.randint(1,5))
        s.release()
    
    if __name__ == '__main__':
        for i in range(10):
            t=Thread(target=task,args=(i,))
            t.start()

    4、事件

    比如:一些线程需要等待另一些线程完成才可以操作, 就类似发送信号一样。

    from threading import Thread,Event
    import time,random
    event=Event()
    def light():
        print("红灯")
        time.sleep(3)
        print('绿灯')
        event.set() #发送信号
    
    def car(name):
        print('%s车正在等红灯'%name)
        event.wait()  #等待对方发信号过来
        print('%s车过绿灯'%name)
    
    if __name__ == '__main__':
        t=Thread(target=light)
        t.start()
        for i in range(10):
            t1=Thread(target=car,args=(i,))
            t1.start()

    5、线程池、进程池:

    池的概念:就是保证计算硬件的安全前提下,最大限度的利用计算机。
    它降低运行效率,但是,保证了计算机硬件的安全。
    注意: 池里面原有的线程或进程是不会重复出现创建和销毁的过程。

    #线程池的创建:
    pool=ThreadPoolExecutor()
    #括号内为线程池中进程的个数,你可以自己设置,默认是5个,最大不会超过32个
    ThreadPoolExecutor类中的参数max_workers就是池中线程数:初始设置代码如下:
    max_workers = min(32, (os.cpu_count() or 1) + 4)
    
    #进程池的创建:
    pool=ProcessPoolExecutor()
    #它的参数如下:os.cpu_count()表示电脑的CPU核数
    self._max_workers = os.cpu_count() or 1
    
    总结:关键代码如下:
    from concurrent.futures import ThreadPoolExecutor,ProcessPoolExecutor
    pool=ProcessPoolExecutor()
    pool=ThreadPoolExecutor(5)
    res=pool.submit(task,i).add_done_callback(call_back)

    进程/线程池的例子如下:

    # -*-coding:utf-8 -*-
    from concurrent.futures import ThreadPoolExecutor,ProcessPoolExecutor
    import time,os
    
    # pool=ThreadPoolExecutor(5)
    pool=ProcessPoolExecutor()
    
    def call_back(n):
        print(">>:",n.result())
    def task(n):
        print(n,os.getpid())
        time.sleep(2)
        return n*n
    
    
    if __name__ == '__main__':
        # list_pool=[]
        for i in range(10):
            res=pool.submit(task,i).add_done_callback(call_back)

    6、协程、gevent模块:

    实质:就是一个思想,它是由程序员自己在代码层面上检测IO操作,一旦遇到IO操作就会在代码级别上完成切换
    注意:IO操作下的切换才是提高效率的,非IO操作下的切换会降低效率。

    gevent模块的使用需要打猴子补丁,不然不会检测到像time.sIeep()等O操作

    #猴子补丁
    from gevent import monkey
    monkey.patch_all()

    具体例子如下:

    # -*-coding:utf-8 -*-
    #猴子补丁
    from gevent import monkey
    monkey.patch_all()
    
    from gevent import spawn
    import time
    
    
    def ha():
        print('hahaha')
        time.sleep(2)
        print('hahaha')
    
    def xixi():
        print('xixi')
        time.sleep(3)
        print('xixi')
    
    if __name__ == '__main__':
        start_time=time.time()
        g1=spawn(ha)
        g2=spawn(xixi)
        g1.join()
        g2.join()
        print(time.time()-start_time)

    7、基于协程的TCP并发:

    服务端例子如下:

     1 # -*-coding:utf-8 -*-
     2 #猴子补丁
     3 from gevent import monkey;monkey.patch_all()
     4 
     5 from gevent import spawn
     6 import socket
     7 
     8 def new_server(ip,addr):
     9     server=socket.socket()
    10     server.bind((ip,addr))
    11     server.listen(5)
    12     while True:
    13         conn,addr=server.accept()
    14         spawn(connect,conn)
    15 
    16 def connect(conn):
    17     while True:
    18         try:
    19             data=conn.recv(1024)
    20             print(data.decode())
    21             if len(data)==0:break
    22             conn.send(data.upper())
    23         except Exception as e:
    24             print(e)
    25             break
    26 if __name__ == '__main__':
    27     g1=spawn(new_server,'localhost',8080)
    28     g1.join()

    客户端代码如下:

     1 # -*-coding:utf-8 -*-
     2 import socket
     3 from threading import Thread,current_thread
     4 
     5 def new_client():
     6     c=socket.socket()
     7     c.connect(('localhost',8080))
     8     while True:
     9         data=('你好,我是%s'%current_thread().name)
    10         c.send(data.encode())
    11         data1=c.recv(1024)
    12         print(data)
    13 
    14 if __name__ == '__main__':
    15     for i in range(200):
    16         t=Thread(target=new_client)
    17         t.start()
    不将就
  • 相关阅读:
    liunx centos下tomcat启动 Cannot find ./catalina.sh
    ls -bash: ls: command not found
    Linux CentOS下安装JDK1.7
    查看Linux Centos 系统信息 内核 CPU 系统版本 磁盘 分区 网络配置 进程 命令
    PHP自动捕捉监控致命错误(500错误) error_get_last() 获取最后一次发生错误信息 register_shutdown_function()在脚本停止执行时注册一个回调函数
    CI CodeIgniter 添加公共函数 全局函数 自定义函数
    express框架封装前戏
    怎奈风云多变换,骚完一波还一波,记PHP mongodb驱动的2019年11月用法
    linux ps sample
    利用python pika库实现rabbitmq客户端
  • 原文地址:https://www.cnblogs.com/nq31/p/13679247.html
Copyright © 2020-2023  润新知