并发编程: GIL锁、GIL与互斥锁区别、进程池与线程池的区别

本文目录:

一、GIL

二、关于GIL性能的讨论

三、计算密集测试

四、IO密集测试

五、GIL与互斥锁

六、进程池

七、进程什么时候算是空闲

八、线程池

 

一、GIL

什么叫GIL

全局解释器锁(GIL Global Interpreter Lock),锁就是线程里面那个锁,锁是为了避免资源竞争造成数据的错乱,python程序的执行过程,1.启动解释器进程  python.exe,
2.解析你的py文件并执行它,每个py程序中都必须有解释器参与解释器其实就是一堆代码,相当于多个线程要调用同一个解释器代码,共享以为竞争 ,竞争就要出事,所以给解释器加锁。

python 中内存管理依赖于 GC(一段用于回收内存的代码) 也需要一个线程
除了你自己开的线程 系统还有一些内置线程   就算你的代码不会去竞争解释器  内置线程也可能会竞争
所以必须加上锁


当一个线程遇到了IO 同时解释器也会自动解锁  去执行其他线程  CPU会切换到其他程序


x = obj  +1
a = obj  +1   2

x = None -1
a = None -1  0

 

二、关于GIL性能的讨论

"""
    解释器加锁以后
        将导致所有线程只能并发 不能达到真正的并行  意味着同一时间只有一个CPU在处理你的线程
        给你的感觉是效率低

    代码执行有两种状态
    阻塞 i/o 失去CPU的执行权   (CPU等待IO完成)
    非阻塞  代码正常执行 比如循环一千万次  中途CPU可能切换 很快会回来   (CPU在计算)

    假如有32核CPU 要处理一个下载任务  网络速度慢 100k/s  文件大小为1024kb
    如果你的代码中IO操作非常多  cpu性能不能直接决定你的任务处理速度


    案例:
        目前有三个任务 每个任务处理需一秒   获取元数据需要一小时
        3个CPU 需要 一小时1秒
        1个cpu 需要 一小时3秒


    在IO密集的程序中 CPU性能无法直接决定程序的执行速度    python就应该干这种活儿
    在计算密集的程序中  CPU性能可以直接决定程序的执行速度

"""

 

三、计算密集测试

from threading import Thread
from multiprocessing import Process
import time


# 计算密集任务

def task1():
    sum = 1
    for i in range(10000000):
        sum *= i


def task2():
    sum = 1
    for i in range(10000000):
        sum *= i


def task3():
    sum = 1
    for i in range(10000000):
        sum *= i


def task4():
    sum = 1
    for i in range(10000000):
        sum *= i


def task5():
    sum = 1
    for i in range(10000000):
        sum *= i


def task6():
    sum = 1
    for i in range(10000000):
        sum *= i

if __name__ == '__main__':

    # 开始时间
    st_time = time.time()
    # 多线程情况下
    # t1 =  Thread(target=task1)
    # t2 = Thread(target=task2)
    # t3 = Thread(target=task3)
    # t4 = Thread(target=task4)
    # t5 = Thread(target=task5)
    # t6 = Thread(target=task6)

    t1 = Process(target=task1)
    t2 = Process(target=task2)
    t3 = Process(target=task3)
    t4 = Process(target=task4)
    t5 = Process(target=task5)
    t6 = Process(target=task6)


    t1.start()
    t2.start()
    t3.start()
    t4.start()
    t5.start()
    t6.start()
    #
    # t1.join()
    # t2.join()
    # t3.join()
    # t4.join()
    # t5.join()
    # t6.join()

    print(time.time() - st_time)

 

四、IO密集测试

from threading import Thread
from multiprocessing import Process
import time


# 计算密集任务
def task1():
    time.sleep(3)


def task2():
    time.sleep(3)


def task3():
    time.sleep(3)


def task4():
    time.sleep(3)


def task5():
    time.sleep(3)


def task6():
    time.sleep(3)

if __name__ == '__main__':

    # 开始时间
    st_time = time.time()
    # 多线程情况下
    # t1 = Thread(target=task1)
    # t2 = Thread(target=task2)
    # t3 = Thread(target=task3)
    # t4 = Thread(target=task4)
    # t5 = Thread(target=task5)
    # t6 = Thread(target=task6)


    t1 = Process(target=task1)
    t2 = Process(target=task2)
    t3 = Process(target=task3)
    t4 = Process(target=task4)
    t5 = Process(target=task5)
    t6 = Process(target=task6)

    t1.start()
    t2.start()
    t3.start()
    t4.start()
    t5.start()
    t6.start()

    # t1.join()
    # t2.join()
    # t3.join()
    # t4.join()
    # t5.join()
    # t6.join()

    print(time.time() - st_time)

 

五、GIL与互斥锁

from  threading import Thread,Lock
import time

mutex = Lock()
num = 1
def task():
    global num
    # print(num)
    mutex.acquire()
    temp = num
    print(temp)
    time.sleep(1)   # 当你们线程中出现io时 GIL锁就解开
    num = temp + 1
    mutex.release()  # 线程任务结束时GIL锁解开


t1 = Thread(target=task,)

t2 = Thread(target=task,)
t1.start()
t2.start()
t1.join()
t2.join()
print(num)

#
# GIL 和 自定义互斥锁的区别
# 全局锁不能保证自己开启的线程安全  但是保证解释器中的数据的安全的
# GIL 在线程调用解释器时 自动加锁  在IO阻塞时或线程代码执行完毕时 自动解锁
#

六、进程池

"""
    进程池
        就是一个装进程的容器
    为什么出现
        当进程很多的时候方便管理进程
    什么时候用?
        当并发量特别大的时候  列入双十一
        很多时候进程是空闲的 就让他进入进程池  让有任务处理时才从进程池取出来使用
    进程池使用
        ProcessPoolExecutor类
        创建时指定最大进程数 自动创建进程
        调用submit函数将任务提交到进程池中
        创建进程是在调用submit后发生的

    总结一下:
        进程池可以自动创建进程
        进程限制最大进程数
        自动选择一个空闲的进程帮你处理任务
        遗留问题 进程什么时候算是空闲

"""
import socket
from multiprocessing import Process

from concurrent.futures import ProcessPoolExecutor

# 收发数据
def task(c, addr):
    while True:
        try:
            data = c.recv(1024)
            print(data.decode("utf-8"))
            if not data:
                c.close()
                break
            c.send(data.upper())
        except Exception:
            print("连接断开")
            c.close()
            break

if __name__ == '__main__':

    server = socket.socket()

    server.bind(("127.0.0.1",65535))

    server.listen(5)

    # 创建一个进程池   默认为CPU个数
    pool = ProcessPoolExecutor()

    while True:
        c,addr = server.accept()
        # p = Process(target=task,args=(c,addr))
        # p.start()
        pool.submit(task,c,addr)

 

七、进程什么时候算是空闲

from concurrent.futures import  ProcessPoolExecutor

import os,time,random

def task():
    time.sleep(random.randint(1,2))
    print(os.getpid())

def run():
    pool = ProcessPoolExecutor(2)
    for i in range(30):
        pool.submit(task)

if __name__ == '__main__':
    run()

 

八、线程池

from concurrent.futures import  ProcessPoolExecutor,ThreadPoolExecutor
from threading import  current_thread

import os,time,random

def task():
    time.sleep(random.randint(1,2))
    print(current_thread())

def run():
    # 默认为cpu核心数 * 5
    pool = ThreadPoolExecutor(3)
    for i in range(30):
        pool.submit(task)

if __name__ == '__main__':
    run()

 

posted @ 2019-01-15 18:11  仗剑煮大虾  阅读(630)  评论(0编辑  收藏  举报