进程池,线程池&协程

线程池与进程池

  池是一个容器的概念,所谓进程池,线程池,就是建立好的进程或线程,在一个容器对象中,且可以重复使用。开启进程与线程都需要消耗资源,两者相比,线程消耗资源更少一些。但是为了能在计算机硬件承受范围内最大限度地利用计算机资源,可以使用线程池与进程池。同时进程池线程池限制了程序云效率,但是保证了计算机安全。

  创建线程池与线程池都在concurrent.future模块下ThreadPoolExeutor(线程池),ProcessPoolExecutor(进程池)。首先需要进线程池创建对象,开启进线程时需要用到submit()方法,且都有返回结果,结果对象需要用到result()获取结果。进线程对象都有shutdown()方法关闭对象。

'''
线程池
    两种方式
'''
import time
from concurrent.futures import ThreadPoolExecutor   # 调用线程池

pool = ThreadPoolExecutor(5)     # 创建线程池对象 线程创建个数默认是当前计算cup个数乘以5,现在设定5个

def func(n):
    print(f'{n}来了')
    time.sleep(2)
    return f'{n}走了'

t_list = []
for i in range(20):   # 开启20个线程
    res = pool.submit(func, f'{i}号伞兵')  # 开启线程, 获取结果对象
    t_list.append(res)  # 添加到列表中
for k in t_list:
    print(k.result())  # 打印结果
'''
线程池处理的第二种方式
'''
import time
from concurrent.futures import ThreadPoolExecutor
# 调用线程池

pool = ThreadPoolExecutor(5)     # 创建线程池对象

def func(n):
    print(f'{n}来了')
    time.sleep(2)
    return f'{n}走了'

def call_back(m):  # 返回结果处理函数
    print(m.result())


for i in range(20):
    pool.submit(func, f'{i}号伞兵').add_done_callback(call_back)   # 使用add_done_callback设置返回结果处理
'''
进程池两种方式
'''
import time
from concurrent.futures import ProcessPoolExecutor
# 调用进程池

pool = ProcessPoolExecutor(5)     # 创建进程池 默认是cup个数

def func(n):
    print(f'{n}来了')
    time.sleep(2)
    return f'{n}走了'

if __name__ == '__main__':
    t = []
    for i in range(20):
        res = pool.submit(func, f'{i}号伞兵')
        t.append(res)
    for k in t:
        print(k.result())
    pool.shutdown()
'''
创建进程第二种方式
'''
import time
from concurrent.futures import ProcessPoolExecutor
# 调用进程池

pool = ProcessPoolExecutor(5)     # 创建进程池 默认是cup个数

def func(n):
    print(f'{n}来了')
    time.sleep(2)
    return f'{n}走了'

def call_back(m):
    print(m.result())

if __name__ == '__main__':
    for i in range(20):
        pool.submit(func, f'{i}号伞兵').add_done_callback(call_back)
进线程对象

协程

  协程是程序员自定义一名词,是对单线程下实现并发,称之为协程。并发是一种现象:是主观上看起来计算机同时执行多个任务。实现并发计算的多道技术就是并发的一种体现,当进程遇到I/O操作时计算机便会保存当前状态,切换到下一个事件的计算。因此要做到切换和保存状态边可以实现并发。在python中使用genvent包中spawn和mokey模块,可以做到保存状态加切换。

import time
from gevent import spawn, monkey;monkey.patch_all()  # monkey.patch_all() 检测I/O

def func1():
    print(111)
    time.sleep(1)   # 模拟I/O阻塞
    print(111)

def func2():
    print(222)
    time.sleep(2)
    print(222)

spawn(func1)      # spawn模块会检测函数中的I/O,检测到I/O状态,就会调到其他非阻塞
res = spawn(func2)  # 检测func2
res.join()   # 等待

利用协程实现单核下TCP并发通信

'''
服务端
'''
import socket
from gevent import spawn, monkey
monkey.patch_all()

server = socket.socket()
server.bind(('127.0.0.1', 8081))
server.listen(5)

def talk(conn):
    while True:
        try:
            data = conn.recv(1024)
            if len(data) == 0: break
            print(data.decode('utf-8'))
            conn.send(data.upper())
        except BaseException as e:
            print(e)
            break
    conn.close()

def server1():
    while True:
        conn, addr = server.accept()
        spawn(talk, conn)   # 检测talk函数,传入conn参数

if __name__ == '__main__':
    res = spawn(server1)  # 检测阻塞态
    res.join()
'''
服务端
'''
import socket
from threading import Thread,current_thread


def clint1():
    clint = socket.socket()
    clint.connect(('127.0.0.1', 8081))
    i = 0
    while True:
        msg = '%s--%s' % (current_thread().name, i)
        clint.send(msg.encode('utf-8'))
        data = clint.recv(1024)
        print(data.decode('utf-8'))
        i += 1

for i in range(400):   # 开启400个线程,访问服务端
    t = Thread(target=clint1)
    print(current_thread().name)
    t.start()

  写成应用应根据任务不同来使用,对于计算密集型的任务来说,不适合用协程,但对于I/O密集型协程是适合的。

  对于并发:可以开多进程,多进程下开多线程,多线程下还可以开多协程!!!

原文地址:https://www.cnblogs.com/huaiXin/p/11358826.html