GIL 线程池 进程池 同步 异步

1.GIL(理论 重点)2.线程池 进程池3.同步 异步

GIL    是一个全局解释器锁,是一个互斥锁    为了防止竞争解释器资源而产生的

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

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

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

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

    如果你的应用程序是大量的IO操作 GIL的影响是微乎其微的    如果你的应用程序时一个大量计算操作 GIL的影响是非常大的 完全无法利用多核cpu
由于有GIL的存在 即使有多个cpu 也不能真正的并行

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

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

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

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

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

import time
def task():
    with open("2.昨日回顾","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)

IO密集型任务


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

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

import time
def task():
    with open("2.昨日回顾","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)

计算密集型任务


应用场景:    TCP程序 应该采用多线程    纯计算 例如人脸识别 语音识别等 采取多进程

既然已经有锁了 还需要自己加锁吗?    什么情况下需要自己加锁 当多个线程需要共享一个不属于解释器资源时 必须要自己家

    不加锁的例子:多个线程要并发修改某一个变量数据

from concurrent.futures import ThreadPoolExecutor

池就是容器服务器不可能无限的开线程,所以需要对线程数量加以控制,线程池就是帮我么封装了线程数量的控制以及线程的创建 销毁 任务的分配

使用方法一样的线程池 在创建时 不会立即开启线程等到提交任务时 如果没有空闲的线程 并且已存在的线程数量小于最大值 开个新的线程开启以后就不会关闭了 直到进程全部结束为止

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") 

同步异步


pool.shutdown(wait=True)该函数也是阻塞函数 关闭线程池等到全部完成在获取结果

原文地址:https://www.cnblogs.com/gengbinjia/p/10496427.html

时间: 2024-07-30 18:53:14

GIL 线程池 进程池 同步 异步的相关文章

python GIL锁、进程池与线程池、同步异步

一.GIL全局解释器锁 全局解释器锁 在CPython中,全局解释器锁(GIL)是一个互斥锁,它可以防止多个本机线程同时执行Python代码.之所以需要这个锁,主要是因为CPython的内存管理不是线程安全的.(然而,自从GIL存在以来,其他特性已经逐渐依赖于它所执行的保证) 什么是GIL 全局解释器锁, 施加在解释器上的互斥锁 为什么需要GIL 由于CPython的内存管理时非线程安全,于是CPython就给解释器加上锁, 解决了安全问题. GIL的加锁与解锁时机 加锁的时机: 在调用解释器时

并发编程 - 线程 - 1.线程queue/2.线程池进程池/3.异步调用与回调机制

1.线程queue :会有锁 q=queue.Queue(3) q.get() q.put() 先进先出 队列后进先出 堆栈优先级队列 1 """先进先出 队列""" 2 import queue 3 q=queue.Queue(3) #先进先出->队列 4 5 q.put('first') 6 q.put(2) 7 # q.put('third') 8 # q.put(4) 9 q.put(4,block=False) #q.put_no

Python并发编程之线程池/进程池--concurrent.futures模块

h2 { color: #fff; background-color: #f7af0d; padding: 3px; margin: 10px 0px } 一.关于concurrent.futures模块 Python标准库为我们提供了threading和multiprocessing模块编写相应的多线程/多进程代码,但是当项目达到一定的规模,频繁创建/销毁进程或者线程是非常消耗资源的,这个时候我们就要编写自己的线程池/进程池,以空间换时间.但从Python3.2开始,标准库为我们提供了conc

线程池&进程池

线程池&进程池 池子解决什么问题? 1.创建/销毁线程伴随着系统开销,如果过于频繁会影响系统运行效率 2.线程并发数量过多,抢占系统资源,从而导致系统阻塞甚至死机 3.能够刚好的控制和管理池子里面的线程和进程 concurrent.futures模块提供了高度封装的异步调用接口 ThreadPoolExecutor:线程池,提供异步调用 ProcessPoolExecutor:进程池,提供异步调用 常用方法 submit(fn, *args, **kwargs):异步提交任务 map(func,

# 进程/线程/协程 # IO:同步/异步/阻塞/非阻塞 # greenlet gevent # 事件驱动与异步IO # Select\Poll\Epoll异步IO 以及selectors模块 # Python队列/RabbitMQ队列

1 # 进程/线程/协程 2 # IO:同步/异步/阻塞/非阻塞 3 # greenlet gevent 4 # 事件驱动与异步IO 5 # Select\Poll\Epoll异步IO 以及selectors模块 6 # Python队列/RabbitMQ队列 7 8 ############################################################################################## 9 1.什么是进程?进程和程序之间有什么

Python3 从零单排28_线程队列&进程池&线程池

1.线程队列 线程队列有三种:先进先出,后进先出,按优先级进出,具体如下: 1 import queue 2 3 # 先进先出 4 q = queue.Queue(3) 5 6 q.put(1) 7 q.put(2) 8 q.put(3) 9 # q.put(4) # 再放阻塞,等待队列消费 10 # q.put(4,block = False) # 不阻塞,强制放数据,如果满的情况下直接报错 等价与 q.put_nowait(4) 11 # q.put(4,block = True) # 阻塞

12 并发编程-(线程)-线程queue&进程池与线程池

queue 英 /kju?/ 美 /kju/ 队列 1.class queue.Queue(maxsize=0) #队列:先进先出 import queue q=queue.Queue() q.put('first') q.put('second') q.put('third') print(q.get()) print(q.get()) print(q.get()) ''' 结果(先进先出): first second third ''' 2.class queue.LifoQueue(max

5.1.24 线程池/进程池实现网络并发

服务端: from socket import * from threading import Thread from concurrent.futures import ThreadPoolExecutor def communicate(conn): while True: try: data=conn.recv(1024) if not data:break conn.send(data.upper()) except ConnectionResetError: break conn.cl

线程(进程)同步--信号量

linux中的信号量既可以用于线程间的同步又可以用于进程间的同步.信号量实际上是一个非负的整数计数器,用来实现对公共资源的控制.在公共资源增加的时候,信号两的值增加:公共资源消耗的时候,信号量的值减少:只有当信号量的值大于大于0的时候,才能访问信号量所带表的公共资源.ps:信号量在linux有posix接口和系统api接口,后者实在是太难记住,所以直接使用前者吧. 有关信号量的主要函数有信号量初始化函数sem_init(3).信号量的销毁函数sem_destroy(1).信号量的增加函数sem_