GIL,进程池,线程池,同步,异步及其相关

1.GIL
    是一个全局解释器锁 ,是一种互斥锁

    为什么需要GIL:因为一个python.exe进程中只有一分解释器,如果这个进程开启了多个线程都要执行代码
        多线程之间要竞争解释器,一旦竞争就有可能出现问题

    带来的问题:同一时间只有一个线程可以访问解释器
    好处:保证了多线程的数据完全

    thread-safe  线程安全的  多个线程同时访问也不会出问题
    not thread-safe 非线程安全的 多个线程同时访问可能会出问题 (加锁)

    默认情况下一个进程只有一个线程 是不会出现问题的 ,但是不要忘记还有GC线程
    一旦出现多个线程就可能出问题 ,所以当初就简单粗暴的加上了GIL锁

    那么多线程是不是完全没有意义?
    由于有GIL的存在 即使有多个CPU 也不能真正的并行

    有三个任务   三个任务要并发执行 使效率最高
    1.多进程
    2.同一个进程下多线程

    只有一个CPU
        如果3个任务都要等待IO
        如果是采用方案1: 由于IO的时间较长 不仅不能提高效率 反而无谓的增加了系统开销
        方案2 更好

    有三个CPU
        如果是采用方案1 并且三个任务都没有IO操作: 开启三个进程 并行的来执行 效率更高
        如果是采用方案2 并且三个任务都没有IO操作: 开三个线程  必须串行执行 所以效率比进程更低

    应用程序分为两种
    1.IO密集型   IO操作较多 纯计算较少   采用多线程
    2.计算密集型 计算操作较多 IO较少     采用多进程

 

IO密集型任务:

from multiprocessing import Process
from threading import  Thread,enumerate,current_thread

import time
def task():
    with open("xxx","rt",encoding="utf-8") as f:
        f.read()

if __name__ == '__main__':
    start = time.time()

    for i in range(100):
        Thread(target=task).start()

    # enumerate是所有的线程
    for t in enumerate():
        if t != current_thread():
            t.join()

    # ps = []
    # for i in  range(100):
    #     p = Process(target=task)
    #     p.start()
    #     ps.append(p)
    #
    # for p in ps:
    #     p.join()

    print(time.time()-start)

 

计算密集型任务:

 

from multiprocessing import Process
from threading import  Thread,enumerate,current_thread

import time
def task():
     a = 1
     for i in range(10000000):
         a += 1

if __name__ == '__main__':
    start = time.time()

    # 采用多线程
    # for i in range(10):
    #     Thread(target=task).start()
    #
    # # enumerate是所有的线程
    # 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程序  应该采用多线程
    纯计算 例如人脸识别 语音识别等  采取多进程

GIL
是什么   为什么出现  如何解决其带来的效率问题

既然已经有锁了 还需要自己加锁吗?
    什么情况需要自己加锁  当多个线程需要共享一个不属于解释器的资源时 必须要自己加
    不用加锁的例子: 多个线程要并发修改某一个变量数据

 

2.线程池 进程池
    池就是容器
    服务器不可能无线的开线程 ,所以需要对线程数量加以控制,线程池就是帮我们封装了线程数量的控制  以及 线程的创建  销毁 任务的分配
    使用方法一样的

    特点:
        线程池 在创建时不会立即开启线程
        等到提交任务时  如果没有空闲线程 并且已存在的线程数量 小于最大值 开个新的

线程开启以后就不会在关闭了  直到进程全部结束为止

from concurrent.futures import ThreadPoolExecutor
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())
import  time

time.sleep(3)
print(threading.active_count())
print(threading.enumerate())

 

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

  指的是程序的执行的状态

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

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

  异步效率高于同步
    发起异步任务的方式 就是线程和进程

  同步和阻塞 是完全不同的:
      阻塞一定是CPU已经切走了
      同步虽然也会卡住 但是CPU没切走 还在你的进程中

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(10):
    res = pool.submit(task,i)
    # res.result() 该函数是阻塞 会一直等到任务执行完毕 导致程序串行执行
    ress.append(res)

# 保证 当我要获取的时候 所有任务都已经执行完毕
pool.shutdown(wait=True) # 该函数也是阻塞函数

# 等到全部完成在获取结果
for i in ress:
    print(i.result())

print("over")

 

 

你可能感兴趣的:(GIL,进程池,线程池,同步,异步及其相关)