• GIL 线程池 进程池 同步 异步 阻塞 非阻塞


    1.GIL

      是一个全局解释器锁,是一种互斥锁
      为什么需要GIL锁:因为一个python.exe进程中只有一份解释器,如果这个进程开启了多个线程都要执行代码
        多线程之间要竞争解释器,一旦竞争就有可能出现问题
      带来的问题:同一时间只有一个线程可以访问解释器
      好处:保证了多线程的数据安全
      thread_safe 线程安全的 多个线程同时访问也不会出问题
      not thread_safe 非线程安全的多线程同时访问可能会出现问题(加锁)
      默认情况下一个进程只有一个线程是不会出现问题的 但是不要忘记还有GC线程
      一旦出现多线程就可能出问题,所以当初就简单粗暴的加上GIL锁

      那么多线程是不是完全没有意义
      由于有GIL的存在 即使有多个CUP 也不能真正的并行
      有三个任务 三个任务要并发执行使效率最高
      1.多进程
      2.同一个进程下多线程
      只有一个CUP
        如果三个任务都要等待IO
        如果是采用方案1:由于IO的时间较长 不仅不能提高效率反而无谓的增加了系统开销
        方案2更好
      有三个CUP
        如果是采用方案1 并且三个任务都没有IO操作:开启三个进程并行的来执行 效率更高
        如果是采用方案2 并且三个任务都没有IO操作:开三个线程 必须串行执行 所以效率比进程更低
        应用程序分为两种
    1.IO密集 IO操作较多 纯计算较少 采用多线程

    from multiprocessing import Process
    from threading import Thread, enumerate, current_thread
    import time
    
    
    def task():
        with open('db', 'rt', encoding='utf-8')as f:
            f.read()
    
    
    if __name__ == '__main__':
        start = time.time()
        # for i in range(100):
        #     Thread(target=task).start()
        #
        # for t in enumerate():
        #     if t != current_thread():
        #         t.join()
        ps = []
        for i in range(100):
            p = Process(target=task)
            p.start()
        for p in ps:
            p.join()
        print(time.time() - start)

    2.计算密集型 计算操作较多 IO较少 采用多进程

    from multiprocessing import Process
    from threading import Thread, enumerate, current_thread
    import time
    import os
    
    
    def task():
    a = 1
    for i in range(10000000):
    a += i
    
    
    if __name__ == '__main__':
    start = time.time()
    for i in range(10):
    Thread(target=task).start()
    
    for t in enumerate():
    if t != current_thread():
    t.join()
    # ps = []
    # for i in range(10):
    # p = Process(target=task)
    # p.start()
    # ps.append(p)
    # for p in ps:
    # p.join()
    print(time.time() - start)

    应用场景:
      TCP程序 应该采用多线程
      纯计算 列入人脸识别 语音识别等 采用多进程
    什么情况需要自己加锁 当多个线程需要共享一个不属于解释器的资源时 必须要自己加
    不用加锁的例子:多个线程要并发修改某个变量数据

    2.线程池 进程池

    from concurrent.futures import ThreadPoolExecutor
    import time
    import threading
    
    
    def task():
    print('running.......')
    
    
    pool = ThreadPoolExecutor(3)
    pool.submit(task)
    pool.submit(task)
    pool.submit(task)
    pool.submit(task)
    print(threading.active_count())
    print(threading.enumerate())
    
    
    time.sleep(3)
    print(threading.active_count())
    print(threading.enumerate())


    池就是容器
    服务器不能无限的开线程,所以需要对线程数量加以控制,
    线程池就帮我们封装了线程数量的控制 以及线程的创建 以及线程的创建 销毁 任务的分配
    使用方法都一样
    线程池再创建时不会立即开启线程
    等到提交任务时 如果没有空闲线程 并且已存在的线程数量 小于最大值 开个新的
    线程开启以后就不会再关闭了 直到进程全部关闭

    3.同步 异步 阻塞 非阻塞
    阻塞:
      程序运行过程中遇到IO操作 无法继续
    非阻塞:
      程序正在运行中,并且没有遇到IO,即使遇到IO也不会阻塞,CPU不会切走
      指的是程序的执行的状态

    同步:
      在发起任务后必须在原地等待 任务执行完毕 才能继续往下执行

    def task():
    for i in range(100000000):
    pass
    
    
    print('start')
    task()
    print('over')

    异步:
      在发起任务后立即继续往下执行,不需要等待任务的执行结果

    # def task():
    # for i in range(100000000):
    # pass
    #
    #
    # print('start')
    # task()
    # print('over')
    from concurrent.futures import ThreadPoolExecutor
    
    pool = ThreadPoolExecutor()
    import time
    
    
    def task(num):
      time.sleep(0.5)
      print('run...')
      return num ** 2
    
    
    ress = []
    for i in range(1, 11):
    res = pool.submit(task, i)
    # res.result() # 该函数是阻塞函数 会一直等到任务执行完毕 导致程序串行执行
    ress.append(res)
    
    # 保证 当我们要获取的时候 所有任务都已经执行完毕
    pool.shutdown(wait=True) # 该函数也是阻塞函数
    # 等到全部完成在获取结果
    for i in ress:
      print(i.result())
    
    print('over')

    指的是发起任务的方式
    异步效率高于同步
    发起异步任务的方式 就是线程和进程
    同步和阻塞是完全不同的
    阻塞一定是CUP已经切走了
    同步虽然也会卡住 但是CUP没切走 还在你的进程中

     并发的TCP

    import socket
    
    from concurrent.futures import ProcessPoolExecutor
    from multiprocessing import Process
    
    
    def task(client):
        while True:
            data = client.recv(1024)
            print(data.decode('utf-8'))
            client.send(data.upper())
    
    
    if __name__ == '__main__':
        server = socket.socket()
        server.bind(('127.0.0.1', 8888))
        server.listen()
        # 创建一个进程池
        pool = ProcessPoolExecutor(3)
        while True:
            client, address = server.accept()  # 三次握手
            # 控制链接数量 得用连接池
            # 开启一个进程来处理这个连接
            # t=Process(target=task,args=(client,))
            # t.start()
            # 现在将任务交到进程池中
            pool.submit(task, client)
    server进程池
    import socket
    
    client = socket.socket()
    client.connect(('127.0.0.1', 8888))
    while True:
        msg = input(">>>:").strip()
        if not msg:
            continue
        client.send(msg.encode('utf-8'))
        print(client.recv(1024).decode('utf-8'))
    client
    import socket
    
    from concurrent.futures import ThreadPoolExecutor
    from threading import Thread
    
    
    def task(client):
        while True:
            data = client.recv(1024)
            print(data.decode('utf-8'))
            client.send(data.upper())
    
    
    if __name__ == '__main__':
        server = socket.socket()
        server.bind(('127.0.0.1', 8888))
        server.listen()
        # 创建一个线程池
        pool = ThreadPoolExecutor(3)
        while True:
            client, address = server.accept()  # 三次握手
            # 控制链接数量 得用连接池
            # 开启一个线程来处理这个连接
            # t=Process(target=task,args=(client,))
            # t.start()
            # 现在将任务交到线程池中
            pool.submit(task, client)
    server线程池
  • 相关阅读:
    我的技术十年
    django-cookie&session
    论学习方法
    操作系统知识
    一百天记录
    Microsoft COCO 数据集
    qt虚拟键盘编译时报错缺乏qpa/qplatforminputcontext.h文件
    ffmpeg拉取rtsp视频流
    顶层QWidget弹窗设置背景为透明,透明部分为黑色。
    ubuntu20.04 gnome桌面系统添加开机自启动GUI程序
  • 原文地址:https://www.cnblogs.com/ShenJunHui6/p/10496721.html
Copyright © 2020-2023  润新知