GIL全局解释器锁、协程、TCP服务端并发

 

一、gevent 模块

       1、monkey      猴子补丁

            monkey.patch_all() # 监听所有的任务是否有IO操作

       2、spawn   是一个方法函数     #spawn是Greenlet类中的spawn方法。def spawn(cls, *args, **kwargs):

一、GIL全局解释器锁(python解释器自带的):本质是一把互斥锁,保证解释器级的数据安全

       在Cpython解释器中,因为Cpython的内存管理不是线程安全。同一个进程下开启的多线程,同一时刻只能有一个线程执行,无法利用多核优势。

       gc:表示的是 垃圾回收机制  (import gc)

 

 

 

 

 

 

 

       GIL全局解释器锁的优缺点:

       1.优点:保证数据的安全

       2.缺点:单个进程下,开启多个线程,牺牲执行效率,无法实现并行,只能实现并发。

       例子:GIL全局解释器锁是python解释器自带的,当没有IO时,同一个进程下开启的多线程,同一时刻只能有一个线程执行。多个线程是 串行。一个线程抢到 python解释器权限锁后,执行完后,释放锁,然后另一个线程再使用。这时进程间的数据是共享的。

from threading import Thread
n = 100
def task():
    global n
    m = n
    n = m - 1
if __name__ == '__main__':
    list1 = []
    for line in range(10):
        t = Thread(target=task)
        t.start()
        list1.append(t)
    for t in list1:
        t.join()
    print(n)                        #90

 

例子:GIL全局解释器锁是python解释器自带的,当遇到IO时,同一个进程下开启的多线程,多个线程是并发。一个线程抢到 python解释器权限锁后,遇到IO后,释放锁,然后另一个线程再使用,遇到IO后,释放锁。。。。       

     

from threading import Thread
import time
n = 100
def task():
    global n
    m = n
    time.sleep(2)
    n = m - 1
if __name__ == '__main__':
    list1 = []
    for line in range(10):
        t = Thread(target=task)
        t.start()
        list1.append(t)
    for t in list1:
        t.join()
    print(n)                         #99

 

二、多进程与多线程的应用

      1.多进程:计算密集型

      2.多线程:IO密集型

     3、IO密集型任务,每个任务4s

        01、单核: 开启多线程比多进程节省资源。

       02、多核:多线程:  - 开启4个子线程: 16s 

              多核:多进程:开启4个进程: 16s + 申请开启资源消耗的时间
    4、计算密集型任务,每个任务4s
        01、单核:开启线程比进程节省资源。
        02、多核:多线程:开启4个子线程: 16s
              多核:多进程: 开启多个进程: 4s

例子:计算密集型:多进程

          IO密集型:多线程

import time
from multiprocessing import Process
def task1():
    i = 10
    for line in range(10000000):
        i += 1
def task2():
    time.sleep(3)
if __name__ == '__main__':                 # 测试多进程:
    start_time = time.time()
    list1 = []
    for line in range(6):
        p = Process(target=task1)         # 计算密集型
        p.start()
        list1.append(p)
    for p in list1:
        p.join()
    end_time = time.time()
    print(f'计算密集型消耗时间: {end_time - start_time}')

    start_time = time.time()
    list1 = []
    for line in range(6):
        p = Process(target=task2)           # IO密集型
        p.start()
        list1.append(p)

    for p in list1:
        p.join()
    end_time = time.time()
    print(f'IO密集型消耗时间: {end_time - start_time}')
    # 结果:
    # 计算密集型消耗时间: 4.038231134414673
    # IO密集型消耗时间: 4.3572492599487305


import time
from threading import Thread
def task1():
    i = 10
    for line in range(10000000):
        i += 1
def task2():
    time.sleep(3)
if __name__ == '__main__':                 # 测试多线程:
    start_time = time.time()
    list1 = []
    for line in range(6):
        t =  Thread(target=task1)         # 计算密集型
        t.start()
        list1.append(t)
    for t in list1:
        t.join()
    end_time = time.time()
    print(f'计算密集型消耗时间: {end_time - start_time}')

    start_time = time.time()
    list1 = []
    for line in range(6):
        t =  Thread(target=task2)           # IO密集型
        t.start()
        list1.append(t)

    for t in list1:
        t.join()
    end_time = time.time()
    print(f'IO密集型消耗时间: {end_time - start_time}')
    结果:
    计算密集型消耗时间: 4.509257555007935
    IO密集型消耗时间: 3.001171827316284
多进程和多线程

 

三、协程:

    1.什么是协程

       进程是资源单位

       线程是执行单位

       协程是单线程下实现并发,协程不是任何单位,只是一个程序员YY出来的东西。

        协程的目的:

        手动实现 "遇到IO切换 + 保存状态" 去欺骗操作系统,让操作系统误以为没有IO操作,将CPU的执行权限给你。

        在IO密集型的情况下,使用协程能提高最高效率。

        例子1、协程实现方法一:

patch_all() 是一个函数方法
spawn() 是类下的一个函数,里面有 start 属性了。所以可以不用写 对象.start 了
from gevent import monkey  # 猴子补丁
monkey.patch_all()              # 监听所有的任务是否有IO操作
from gevent import spawn   # spawn(任务)
import time

def task1():
    print('start from task1...')
    time.sleep(1)
    print('end from task1...')

def task2():
    print('start from task2...')
    time.sleep(3)
    print('end from task2...')

def task3():
    print('start from task3...')
    time.sleep(5)
    print('end from task3...')


if __name__ == '__main__':

    start_time = time.time()
    sp1 = spawn(task1)
    sp2 = spawn(task2)
    sp3 = spawn(task3)
    sp1.start()
    sp2.start()
    sp3.start()
    sp1.join()
    sp2.join()
    sp3.join()
    end_time = time.time()

    print(f'消耗时间: {end_time - start_time}')
结果:
start from task1...
start from task2...
start from task3...
end from task1...
end from task2...
end from task3...
消耗时间: 5.010286569595337        # 因为是多线程,异步(同时)提交任务。并发执行。

Process finished with exit code 0
View Code

例子2、协程实现方法二:

from gevent import monkey  # 猴子补丁
monkey.patch_all()         # 监听所有的任务是否有IO操作
from gevent import spawn   # spawn(任务)
from gevent import joinall
import time
def task1():
    print('start from task1...')
    time.sleep(1)
    print('end from task1...')
def task2():
    print('start from task2...')
    time.sleep(3)
    print('end from task2...')
def task3():
    print('start from task3...')
    time.sleep(5)
    print('end from task3...')
if __name__ == '__main__':
    start_time = time.time()
    sp1 = spawn(task1)
    sp2 = spawn(task2)
    sp3 = spawn(task3)
    joinall([sp1, sp2, sp3])
    end_time = time.time()
    print(f'消耗时间: {end_time - start_time}')

结果:
start from task1...
start from task2...
start from task3...
end from task1...
end from task2...
end from task3...
消耗时间: 5.011286497116089

Process finished with exit code 0
View Code

 四、TCP服务端多线程并发

client 文件


import socket
client = socket.socket()
client.connect(('127.0.0.1', 9000))
print('Client is run....')
while True:
    msg = input('客户端>>:').encode('utf-8')
    client.send(msg)
    data = client.recv(1024)                # 接收数据
    print(data)

 

server 文件


import socket
from concurrent.futures import ThreadPoolExecutor
server = socket.socket()
server.bind(('127.0.0.1', 9000))
server.listen(5)
def run(conn):                              # 封装成一个函数
    while True:
        try:
            data = conn.recv(1024)         # 接收数据
            if len(data) == 0:
                break
            print(data.decode('utf-8'))
            conn.send('111'.encode('utf-8'))
        except Exception as e:
            break
    conn.close()
if __name__ == '__main__':
    print('Server is run....')
    pool = ThreadPoolExecutor(50)         # 线程池
    while True:
        conn, addr = server.accept()     # 反复产生通道
        print(addr)
        pool.submit(run, conn)           # 产生线程。


注意:submit() 是一个对象
posted @ 2019-12-09 10:34  薛定谔的猫66  阅读(424)  评论(0)    收藏  举报