27 Apr 18 GIL 多进程多线程使用场景 线程互斥锁与GIL对比 基于多线程实现并发的套接字通信 进程池与线程池 同步、异步、阻塞、非阻塞

27 Apr 18
一、全局解释器锁 (GIL)
运行test.py的流程:
a、将python解释器的代码从硬盘读入内存
b、将test.py的代码从硬盘读入内存  (一个进程内装有两份代码)
c、将test.py中的代码像字符串一样读入python解释器中解析执行


1 、GIL:全局解释器锁 (CPython解释器的特性)
In CPython, the global interpreter lock, or GIL, is a mutex that prevents multiple
native threads from executing Python bytecodes at once. This lock is necessary mainly
because CPython’s memory management (垃圾回收机制,由解释器定期执行)is not thread-safe(如果不是串行改数据,当x=10的过程中内存中产生一个10,还没来的及绑定x,就有可能被垃圾回收机制回收).However, since the GIL exists, other features have grown to depend on the guarantees that it enforces.)
GIL本质就是一把夹在解释器身上的互斥锁(执行权限)。同一个进程内的所有线程都需要先抢到GIL锁,才能执行解释器代码
 
2、GIL的优缺点:
优点:保证Cpython解释器内存管理的线程安全
缺点:在Cpython解释器中,同一个进程下开启的多线程,同一时刻只能有一个线程执行,也就说Cpython解释器的多线程无法实现并行无法利用多核优势
 
注意:
a、GIL不能并行,但有可能并发,不一定为串行。因为串行是一个任务完完全全执行完毕后才进行下一个;而cpython中,一个线程在io时,被CPU释放时,会被强行取消GIL的使用权限
b、多核(多CPU)的优势是提升运算效率
c、计算密集型--》使用多进程,以用上多核
d、IO密集型--》使用多线程
 
二、Cpython解释器并发效率验证
1、计算密集型应该使用多进程
from multiprocessing import Process
from threading import Thread
 
import time
# import os
# print(os.cpu_count())  #查看cpu个数
 
def task1():
    res=0
    for i in range(1,100000000):
        res+=i
 
def task2():
    res=0
    for i in range(1,100000000):
        res+=i
 
def task3():
    res=0
    for i in range(1,100000000):
        res+=i
 
def task4():
    res=0
    for i in range(1,100000000):
        res+=i
 
if __name__ == ‘__main__‘:
    # p1=Process(target=task1)
    # p2=Process(target=task2)
    # p3=Process(target=task3)
    # p4=Process(target=task4)
 
    p1=Thread(target=task1)
    p2=Thread(target=task2)
    p3=Thread(target=task3)
    p4=Thread(target=task4)
    start_time=time.time()
    p1.start()
    p2.start()
    p3.start()
    p4.start()
    p1.join()
    p2.join()
    p3.join()
    p4.join()
    stop_time=time.time()
    print(stop_time - start_time)
 
2、IO密集型应该使用多线程
from multiprocessing import Process
from threading import Thread
 
import time
 
def task1():
    time.sleep(3)
 
def task2():
    time.sleep(3)
 
def task3():
    time.sleep(3)
 
def task4():
    time.sleep(3)
 
if __name__ == ‘__main__‘:
    # p1=Process(target=task1)
    # p2=Process(target=task2)
    # p3=Process(target=task3)
    # p4=Process(target=task4)
 
    # p1=Thread(target=task1)
    # p2=Thread(target=task2)
    # p3=Thread(target=task3)
    # p4=Thread(target=task4)
    # start_time=time.time()
    # p1.start()
    # p2.start()
    # p3.start()
    # p4.start()
    # p1.join()
    # p2.join()
    # p3.join()
    # p4.join()
    # stop_time=time.time()
    # print(stop_time - start_time) #3.138049364089966
 
    p_l=[]
    start_time=time.time()
 
    for i in range(500):
        p=Thread(target=task1)
        p_l.append(p)
        p.start()
 
    for p in p_l:
        p.join()
 
print(time.time() - start_time)
 
三、线程互斥锁与GIL对比
GIL能保护解释器级别代码(和垃圾回收机制有关)但保护不了其他共享数据(比如自己的代码)。所以在程序中对于需要保护的数据要自行加锁
 
from threading import Thread,Lock
import time
 
mutex=Lock()
count=0
 
def task():
    global count
    mutex.acquire()
    temp=count
    time.sleep(0.1)
    count=temp+1
    mutex.release()
 
if __name__ == ‘__main__‘:
    t_l=[]
    for i in range(2):
        t=Thread(target=task)
        t_l.append(t)
        t.start()
    for t in t_l:
        t.join()
 
    print(‘主‘,count)
 
四、基于多线程实现并发的套接字通信
服务端:
from socket import *
from threading import Thread
from concurrent.futures import ProcessPoolExecutor,ThreadPoolExecutor
 
tpool=ThreadPoolExecutor(3)  #进程和线程都不能无限多,导入模块来限制进程和线程池重点数目;进程线程池中封装了Process、Thread模块的功能
 
def communicate(conn,client_addr):
    while True:  # 通讯循环
        try:
            data = conn.recv(1024)
            if not data: break
            conn.send(data.upper())
        except ConnectionResetError:
            break
    conn.close()
 
def server():
    server=socket(AF_INET,SOCK_STREAM)
    server.bind((‘127.0.0.1‘,8080))
    server.listen(5)
 
    while True: # 链接循环
        conn,client_addr=server.accept()
        print(client_addr)
        # t=Thread(target=communicate,args=(conn,client_addr))
        # t.start()
        tpool.submit(communicate,conn,client_addr)
 
    server.close()
 
if __name__ == ‘__main__‘:
    server()
 
客户端:
from socket import *
 
client=socket(AF_INET,SOCK_STREAM)
client.connect((‘127.0.0.1‘,8080))
 
while True:
    msg=input(‘>>>: ‘).strip()
    if not msg:continue
    client.send(msg.encode(‘utf-8‘))
    data=client.recv(1024)
    print(data.decode(‘utf-8‘))
 
client.close()
 
五、进程池与线程池
为什么要用“池”:池子使用来限制并发的任务数目,限制我们的计算机在一个自己可承受的范围内去并发地执行任务
池子内什么时候装进程:并发的任务属于计算密集型
池子内什么时候装线程:并发的任务属于IO密集型
 
1、进程池
from concurrent.futures import ProcessPoolExecutor,ThreadPoolExecutor
import time,os,random
 
def task(x):
    print(‘%s 接客‘ %os.getpid())
    time.sleep(random.randint(2,5))
    return x**2
 
if __name__ == ‘__main__‘:
    p=ProcessPoolExecutor() # 默认开启的进程数是cpu的核数
 
    # alex,武佩奇,杨里,吴晨芋,张三
 
    for i in range(20):
        p.submit(task,i)
 
2、线程池
from concurrent.futures import ProcessPoolExecutor,ThreadPoolExecutor
import time,os,random
 
def task(x):
    print(‘%s 接客‘ %x)
    time.sleep(random.randint(2,5))
    return x**2
 
if __name__ == ‘__main__‘:
    p=ThreadPoolExecutor(4) # 默认开启的线程数是cpu的核数*5
 
    # alex,武佩奇,杨里,吴晨芋,张三
 
    for i in range(20):
        p.submit(task,i)
 
六、同步、异步、阻塞、非阻塞
1、阻塞与非阻塞指的是程序的两种运行状态
阻塞:遇到IO就发生阻塞,程序一旦遇到阻塞操作就会停在原地,并且立刻释放CPU资源
非阻塞(就绪态或运行态):没有遇到IO操作,或者通过某种手段让程序即便是遇到IO操作也不会停在原地,执行其他操作,力求尽可能多的占有CPU
 
2、同步与异步指的是提交任务的两种方式:
同步调用:提交完任务后,就在原地等待,直到任务运行完毕后,拿到任务的返回值,才继续执行下一行代码
异步调用:提交完任务后,不在原地等待,直接执行下一行代码。等全部执行完毕后取出结果
 
from concurrent.futures import ProcessPoolExecutor,ThreadPoolExecutor
import time,os,random
 
def task(x):
    print(‘%s 接客‘ %x)
    time.sleep(random.randint(1,3))
    return x**2
 
if __name__ == ‘__main__‘:
    # 异步调用
    p=ThreadPoolExecutor(4) # 默认开启的线程数是cpu的核数*5
 
    # alex,武佩奇,杨里,吴晨芋,张三
 
    obj_l=[]
    for i in range(10):
        obj=p.submit(task,i)
        obj_l.append(obj)
 
    # p.close()
    # p.join()
    p.shutdown(wait=True) (等同于p.close()(不允许向池中放新任务) + p.join())
 
    print(obj_l[3].result())
    print(‘主‘)
 
    # 同步调用
    p=ThreadPoolExecutor(4) # 默认开启的线程数是cpu的核数*5
 
    # alex,武佩奇,杨里,吴晨芋,张三
 
    for i in range(10):
        res=p.submit(task,i).result()
 
    print(‘主‘)

原文地址:https://www.cnblogs.com/zhangyaqian/p/py20180427.html

时间: 2025-01-04 05:47:15

27 Apr 18 GIL 多进程多线程使用场景 线程互斥锁与GIL对比 基于多线程实现并发的套接字通信 进程池与线程池 同步、异步、阻塞、非阻塞的相关文章

Python Threading 线程/互斥锁/死锁/GIL锁

导入线程包 import threading 准备函数线程,传参数 t1 = threading.Thread(target=func,args=(args,)) 类继承线程,创建线程对象 class MyThread(threading.Thread) def run(self): pass if __name__ == "__main__": t = MyThread() t.start() 线程共享全面变量,但在共享全局变量时会出现数据错误问题使用 threading 模块中的

python 之 并发编程(进程池与线程池、同步异步阻塞非阻塞、线程queue)

9.11 进程池与线程池 池子使用来限制并发的任务数目,限制我们的计算机在一个自己可承受的范围内去并发地执行任务 池子内什么时候装进程:并发的任务属于计算密集型 池子内什么时候装线程:并发的任务属于IO密集型 进程池: from concurrent.futures import ProcessPoolExecutor,ThreadPoolExecutor import time,os,random ? def task(x): print('%s 接客' %os.getpid()) time.

异步/同步,阻塞/非阻塞,单线程/多线程概念梳理

最近看了一文说到异步是单线程的,顿时就傻眼了,对相关概念和同事进行了一些讨论和总结. 文中的描述是以我的理解来说的,可能不一定准确甚至正确,有错欢迎指正. 这三个概念我认为是描述不同的维度的,概念正交. 异步 同步 异步和同步是不同的流程设计风格. 但存在依赖关系的操作之间是同步的,也就是如果操作B依赖操作A的返回,那么B必须要在A结束后才能执行. 比如你要读取文件然后对文件内容进行处理,那么读取内容和处理内容就是同步的. 而异步这是操作间没有依赖关系,或者先后顺序并不重要. 比如用户登陆要给登

# 进程/线程/协程 # 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.什么是进程?进程和程序之间有什么

[Z] linux基础编程:IO模型:阻塞/非阻塞/IO复用 同步/异步 Select/Epoll/AIO

原文链接:http://blog.csdn.net/colzer/article/details/8169075 IO概念 Linux的内核将所有外部设备都可以看做一个文件来操作.那么我们对与外部设备的操作都可以看做对文件进行操作.我们对一个文件的读写,都通过调用内核提供的系统调用:内核给我们返回一个file descriptor(fd,文件描述符).而对一个socket的读写也会有相应的描述符,称为socketfd(socket描述符).描述符就是一个数字,指向内核中一个结构体(文件路径,数据

linux基础编程:IO模型:阻塞/非阻塞/IO复用 同步/异步 Select/Epoll/AIO(转载)

IO概念 Linux的内核将所有外部设备都可以看做一个文件来操作.那么我们对与外部设备的操作都可以看做对文件进行操作.我们对一个文件的读写,都通过调用内核提供的系统调用:内核给我们返回一个file descriptor(fd,文件描述符).而对一个socket的读写也会有相应的描述符,称为socketfd(socket描述符).描述符就是一个数字,指向内核中一个结构体(文件路径,数据区,等一些属性).那么我们的应用程序对文件的读写就通过对描述符的读写完成. linux将内存分为内核区,用户区.l

30分钟读懂进程线程、同步异步、阻塞非阻塞、并发并行

基本概念 1 进程和线程 进程(Process): 是Windows系统中的一个基本概念,它包含着一个运行程序所需要的资源.一个正在运行的应用程序在操作系统中被视为一个进程,进程可以包括一个或多个线程.线程是操作系统分配处理器时间的基本单元,在进程中可以有多个线程同时执行代码.进程之间是相对独立的,一个进程无法访问另一个进程的数据(除非利用分布式计算方式),一个进程运行的失败也不会影响其他进程的运行,Windows系统就是利用进程把工作划分为多个独立的区域的.进程可以理解为一个程序的基本边界.是

操作系统介绍-操作系统历史,IO,进程的三态,同步异步阻塞非阻塞

1.操作系统历史 2.进程,IO,同步异步阻塞非阻塞 操作系统历史: 手工操作: 1946年第一台计算机诞生--20世纪50年代中期,计算机工作还在采用手工操作方式.此时还没有操作系统的概念. 手工操作方式两个特点: (1)用户独占全机.不会出现因资源已被其他用户占用而等待的现象,但资源的利用率低. (2)CPU 等待手工操作.CPU的利用不充分. 批处理-磁带操作: 主机与输入机之间增加一个存储设备--磁带,在运行于主机上的监督程序的自动控制下,计算机可自动完成:成批地把输入机上的用户作业读入

(转)同步异步/阻塞非阻塞 和 5种linux网络通信模型

会阻塞的函数:connect, accept,send/recv/sendto/recvfrom等读写函数. 不会阻塞的函数:bind, listen,socket, closesocket. linux网络通信模型有: 阻塞IO模型(同步),非阻塞IO模型(拷贝同步),IO复用模型(多线程同步),信号驱动IO模型((拷贝同步),异步IO模型(异步). node.js对同步/异步,阻塞非阻塞的解释: 线程在执行中如果遇到磁盘读写或网络通信(统称为I/O 操作),通常要耗费较长的时间,这时 操作系