• 线程通信


    1. 死锁现象与递归锁

      死锁现象举例
      
      from threading import Thread
      from threading import Lock
      import time
      
      lock_A = Lock()
      lock_B = Lock()
      
      class MyThread(Thread):
          def run(self):
              self.f1()
              self.f2()
      
          def f1(self):
              lock_A.acquire()
              print(f'{self.name}抢到了A锁')
              lock_B.acquire()
              print(f'{self.name}抢到了B锁')
              lock_B.release()
              lock_A.release()
          def f2(self):
              lock_B.acquire()
              print(f'{self.name}抢到了B锁')
              time.sleep(0.1)
              lock_A.acquire()
              print(f'{self.name}抢到了A锁')
              lock_A.release()
              lock_B.release()
      if __name__ == '__main__':
          for i in range(3):
              t = MyThread()
              t.start()
      

      1566547259659

      递归锁
      # 递归锁有一个计数的功能, 原数字为0,上一次锁,计数+1,释放一次锁,计数-1,
      # 只要递归锁上面的数字不为零,其他线程就不能抢锁.
      #递归锁可以解决死锁现象,业务需要多个锁时,先要考虑递归锁
      
      from threading import Thread
      from threading import RLock
      import time
      
      lock_A = lock_B = RLock()
      
      
      class MyThread(Thread):
          def run(self):
              self.f1()
              self.f2()
      
          def f1(self):
              lock_A.acquire()
              print(f'{self.name}抢到了A锁')
              lock_B.acquire()
              print(f'{self.name}抢到了B锁')
              lock_B.release()
              lock_A.release()
          def f2(self):
              lock_B.acquire()
              print(f'{self.name}抢到了B锁')
              time.sleep(0.1)
              lock_A.acquire()
              print(f'{self.name}抢到了A锁')
              lock_A.release()
              lock_B.release()
      if __name__ == '__main__':
          for i in range(3):
              t = MyThread()
              t.start()
      
    2. 信号量

      2.1 引入Semaphore模块,Semaphore(5)相当于有五把锁同时让人去抢到,之后释放掉几把锁就会有几把锁被抢

      2.2 也是一种锁,控制并发数量

      from threading import Thread,Semaphore,current_thread
      import time
      import random
      sem = Semaphore(5)
      def task():
          sem.acquire()
          print(f'{current_thread().name} 厕所ing')
          time.sleep(random.randint(1,3))
      
          sem.release()
      if __name__ == '__main__':
          for i in range(20):
              t = Thread(target=task,)
              t.start()
      
    3. GIL全局解释器锁

      1. 好多自称大神的说,GIL锁就是python的致命缺陷,Python不能多核,并发不行等等 .....

        1566547908378

      2. 理论上来说:单个进程的多线程可以利用多核.但是,开发Cpython解释器的程序员,给进入解释器的线程加了锁.1566547928207

      3. 为什么加锁?

        1. 当时都是单核时代,而且cpu价格非常贵.
        2. 如果不加全局解释器锁, 开发Cpython解释器的程序员就会在源码内部各种主动加锁,解锁,非常麻烦,各种死锁现象等等.他为了省事儿,直接进入解释器时给线程加一个锁.
        3. 优点: 保证了Cpython解释器的数据资源的安全.
        4. 缺点: 单个进程的多线程不能利用多核.
      4. Jpython没有GIL锁.pypy也没有GIL锁.

      5. 现在多核时代, 我将Cpython的GIL锁去掉行么?

        1. 因为Cpython解释器所有的业务逻辑都是围绕着单个线程实现的,去掉这个GIL锁,几乎不可能.1566547961588
        2. 单个进程的多线程可以并发,但是不能利用多核,不能并行.
        3. 多个进程可以并发,并行.
        4. io密集型:1566547980018
        5. 计算密集型:1566547995439
    4. GIL与lock锁的区别

      1. 相同点: 都是同种锁,互斥锁.

      2. 不同点:

        ​ 1. GIL锁全局解释器锁,保护解释器内部的资源数据的安全.

        ​ 2. GIL锁 上锁,释放无需手动操作.

        ​ 3. 自己代码中定义的互斥锁保护进程中的资源数据的安全.

        ​ 4. 自己定义的互斥锁必须自己手动上锁,释放锁.1566548019018

    5. 验证计算密集型IO密集型的效率

      1. o密集型:

      1566548054638

      1. 计算密集型:

      1566548075660

      1. 代码验证:

        1. 计算密集型:多进程并发并行效率高
        from threading import Thread
        from multiprocessing import Process
        import random
        import time
        ##计算密集型:单个进程的多线程并发vs多个进程并发,并行
        def task():
            count = 0
            for i in range(100000000):
                count += 1
        #多进程的并发,并行
        if __name__ == '__main__':
            # start_time = time.time()
            # l1 = []
            # for i in range(4):
            #     p = Process(target=task)
            #     l1.append(p)
            #     p.start()
            # for i in l1:
            #     i.join()
            # print(f'执行效率:{time.time()-start_time}')#执行效率:14.23704481124878
        
            #多线程并发
            start_time = time.time()
            l1 = []
            for i in range(4):
                p = Thread(target=task)
                l1.append(p)
                p.start()
            for e in l1:
                e.join()
            print(f'执行效率:{time.time()-start_time}')#执行效率:25.48371386528015
           
        
        1. IO密集型:单个进程多线程并发效率高
        from threading import Thread
        from multiprocessing import Process
        import random
        import time
        def task():
            count = 0
            time.sleep(random.randint(1,3))
            count += 1
        #多进程的并发,并行
        # if __name__ == '__main__':
            # start_time = time.time()
            # l1 = []
            # for i in range(50):
            #     p = Process(target=task,)
            #     l1.append(p)
            #     p.start()
            # for e in l1:
            #     e.join()
            # print(f'执行效率:{time.time()-start_time}')#执行效率:5.1458330154418945
        #单个进程多线程并发
        if __name__ == '__main__':
            start_time = time.time()
            li = []
            for i in range(50):
                t = Thread(target=task,)
                li.append(t)
                t.start()
            for e in li:
                e.join()
            print(f'执行效率:{time.time()-start_time}')#执行效率:3.0080912113189697
        
        
      2. 计算密集型:

    6. 多线程实现socket通信

      1. 无论是多线程还是多进程,如果按照上面的写法,来一个客户端请求,我就开一个线程,来一个请求开一个线程,
      2. 应该是这样: 你的计算机允许范围内,开启的线程进程数量越多越好.
      server端
      
      import socket
      from threading import Thread
      
      
      def communicate(conn,addr):
          while 1:
              try:
                  from_client_data = conn.recv(1024)
                  print(f'收到来自客户端{addr[0]}的信息:{from_client_data.decode("utf-8")}')
      
                  to_client_data = input('>>>').strip()
                  conn.send(to_client_data.encode('utf-8'))
              except Exception:
                  break
          conn.close()
      def _accept():
          server = socket.socket()
          
          server.bind(('127.0.0.1', 8849))
          
          server.listen(5)
          while 1:
              conn, addr = server.accept()
              t = Thread(target=communicate,args=(conn,addr))
              t.start()
      if __name__ == '__main__':
          _accept()
      
      client端
      
      import socket
      client = socket.socket()
      client.connect(('127.0.0.1',8849))
      while 1:
              try:
                  to_server_data = input('>>>').strip()
                  client.send(to_server_data.encode('utf-8'))
      
                  from_server_data = client.recv(1024)
                  print(f'来自客户端的消息:{from_server_data.decode("utf-8")}')
      
              except Exception:
                  break
      client.close()
      
    7. 进程池,线程池

      1. 线程池: 一个容器,这个容器限制住你开启线程的数量,比如4个,第一次肯定只能并发的处理4个任务,只要有任务完成,线程马上就会接下一个任务.
      2. 以时间换空间.
      from concurrent.futures import ProcessPoolExecutor, ThreadPoolExecutor
      import os
      import time
      import random
      
      # print(os.cpu_count())
      def task(n):
          print(f'{os.getpid()} 接客')
          time.sleep(random.randint(1,3))
      
      
      if __name__ == '__main__':
      
          # 开启进程池  (并行(并行+并发))
          # p = ProcessPoolExecutor()  # 默认不写,进程池里面的进程数与cpu个数相等
          #
          # # p.submit(task,1)
          # # p.submit(task,1)
          # # p.submit(task,1)
          # # p.submit(task,1)
          # # p.submit(task,1)
          # # p.submit(task,1)
          # # p.submit(task,1)
          # for i in range(20):
          #     p.submit(task,i)
          #
          # 开启线程池  (并发)
          t = ThreadPoolExecutor()  # 默认不写, cpu个数*5 线程数
          # t = ThreadPoolExecutor(100)  # 100个线程
      
          for i in range(20):
              t.submit(task,i)
      

      结合:

      server
      from concurrent.futures import ThreadPoolExecutor
      from threading import Thread
      import socket
      
      def communicate(conn,addr):
          while 1:
              try:
                  from_client_data = conn.recv(1024)
                  print(f'收到来自客户端{addr}的信息:{from_client_data.decode("utf-8")}')
      
                  to_client_data = input('>>>').strip()
                  conn.send(to_client_data.encode('utf-8'))
              except Exception:
                  break
          conn.close()
      def _accept():
          server = socket.socket()
      
          server.bind(('127.0.0.1', 8840))
      
          server.listen(5)
      
          a = ThreadPoolExecutor(2)
          while 1:
              conn, addr = server.accept()
              t = Thread(target=communicate,args=(conn,addr))
              a.submit(communicate,conn,addr)
      
      if __name__ == '__main__':
          _accept()
      
      import socket
      client = socket.socket()
      client.connect(('127.0.0.1',8840))
      while 1:
              try:
                  to_server_data = input('>>>').strip()
                  client.send(to_server_data.encode('utf-8'))
      
                  from_server_data = client.recv(1024)
                  print(f'来自客户端的消息:{from_server_data.decode("utf-8")}')
      
              except Exception:
                  break
      client.close()
  • 相关阅读:
    今日进度
    今日进度
    每周总结
    今日进度
    今日进度
    今日进度
    flask展示Excel
    ubuntu 相关
    python解析xml三种方法【ElementTree】【DOM】【SAX】
    Lambda实现if...elif...else【三元表达式】
  • 原文地址:https://www.cnblogs.com/-777/p/11403749.html
Copyright © 2020-2023  润新知