【0919 | Day 35】线程queue/线程池和进程池/协程

socket多线程

客户端

import socket
from threading import Thread

def client_demo():
    client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    
    client.connect(('192.168.11.199', 8010))
    while True:
        msg = f'{currentThread().name}'
        if len(msg) == 0: break
        client.send(msg.encode('utf-8'))
        feedback = client.recv(1024)
        print(feedback.decode('utf-8'))
     
    client.close()
    
if __name__ == '__main__':
    for i in range(5):
        t = Thread(target=client_demo)
        t.start()
            

服务端

import socket
from threading import Thread

def talk(conn):
    while True:
        try:
            msg = conn.recv(1024)
            if len(msg) == 0: break
            conn.send(msg.upper())
        except connectionResetError
        	print('客户端关闭了一个链接')
            break
    conn.close()
    
    
def serve_demo():
    server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    server.bind(('192.168.11.199', 8010))
    server.listen(5)
    
    while True:
        conn, addr = server.accept()
        t = Thread(target=talk, args(conn,))
        t.start()
        
if __name__ == '__main__':
    server_demo()

线程queue

用法一:先进先出(Queue)

import queue

q = queue.Queue()
q.put('123')
q.put('qweqwe')
print(q.get())
print(q.get())
q.task_done()
q.task_done()

123
qweqwe

用法二:先进后出(LifoQueue)

堆栈

import queue

q = queue.LifoQueue() 
q.put('粉红色的背心儿')
q.put('粉红色的裤子')
q.put('欧文的各种设备')
print(q.get())
print(q.get())
print(q.get())

欧文的各种设备
粉红色的裤子
粉红色的背心儿

用法三:优先级(PriorityQueue)

通常这个元组的第一个值是int类型

import queue

# 可以根据优先级取数据
q = queue.PriorityQueue() 
q.put((50, '吴磊'))
q.put((80, '陈飞宇'))
q.put((1, '欧阳娜娜'))
print(q.get())
print(q.get())
print(q.get())

(1, '欧阳娜娜')
(50, '吴磊')
(80, '陈飞宇')

线程定时器

介绍

启动某个线程的倒计时

from threading import Thread
import time

def task():
    print('线程执行!')
    time.sleep(1)
    print('线程结束!')
    

t = Timer(3, task) #3秒后执行task
t.start()

==》时间过去3秒

线程执行了

==》时间过去2秒

线程结束了

进程池和线程池

介绍

  • 池:限制进程数或线程数
  • 限制时间:当并发的任务数量远远大于计算机所能承受的范围,即无法一次性开启过多的任务数量

例子(进程池)

from concurrent.future import ProcessPoolExecutor, ThreadPoolExecutor
from threading import currentThread
from multiprocessing import current_process
import time

def task(i):
    print(f'{current_process().name}在执行任务{i}')
    time.sleep(1)
    return i**2

if __name__ == '__main__':
    pool = ProcessPoolExecutor(3) # 池子里只有3个进程
    fu_list = []
    for i in range(9):
        future = pool.submit(task, i) # task任务要做9次, 3个进程负责做这个事
        fu_list.append(future)
    pool.shutdown() # 关闭了池的入口,会等待所有的任务执行完,结束阻塞
    for fu in fu_list:
        print(fu.result())

进程 SpawnProcess-1 在执行任务 0
进程 SpawnProcess-2 在执行任务 1
进程 SpawnProcess-3 在执行任务 2

进程 SpawnProcess-1 在执行任务 3
进程 SpawnProcess-2 在执行任务 4
进程 SpawnProcess-3 在执行任务 5

进程 SpawnProcess-1 在执行任务 6
进程 SpawnProcess-2 在执行任务 7
进程 SpawnProcess-3 在执行任务 8

0
1
4
9
16
25
36
49
64

例子(线程池)

from concurrent.future import ProcessPoolExecutor, ThreadPoolExecutor
from threading import currentThread
from multiprocessing import current_process
import time

def task(i):
    print(f'{currentThread().name}在执行任务{i}')
    time.sleep(1)
    return i**2

if __name__ == '__main__':
    pool = ThreadPoolExecutor(3) # 池子里只有3个线程
    fu_list = []
    for i in range(9):
        future = pool.submit(task, i) # task任务要做9次, 3个线程负责做这个事
        fu_list.append(future)
    pool.shutdown() # 关闭了池的入口,会等待所有的任务执行完,结束阻塞
    for fu in fu_list:
        print(fu.result())

ThreadPoolExecutor-0_0 在执行任务 0
ThreadPoolExecutor-0_1 在执行任务 1
ThreadPoolExecutor-0_2 在执行任务 2

ThreadPoolExecutor-0_2 在执行任务 3
ThreadPoolExecutor-0_0 在执行任务 4
ThreadPoolExecutor-0_1 在执行任务 5

ThreadPoolExecutor-0_0 在执行任务 6
ThreadPoolExecutor-0_1 在执行任务 7
ThreadPoolExecutor-0_2 在执行任务 8

0
1
4
9
16
25
36
49
64

同步和异步

介绍

  • 同步:提交了一个任务,必须等任务执行完拿到返回值,才能执行下一行代码(一般不使用)
  • 异步:提交了一个任务,不等执行完,就可以直接执行下一行代码

协程

介绍

什么是协程?

  • 单线程下实现并发

为什么要有协程?

  • 协程本质是程序员抽象出来的概念,操作系统根本不知道协程的存在

    • 操作系统 == 佛祖

    • 线程 == 孙悟空

    • 协程 == 猴毛

  • 通俗的说,孙悟空的一切活动都在佛祖的股掌之中。一旦某个小猴子遇到io或者磨磨蹭蹭耽误太长时间了,就需要交出自己的CPU执行权限(金箍棒),由别的猴子(线程)去做

  • 可是这样,孙悟空觉得不服气,凭什么???我也不想等那么长时间的io啊!因此,孙悟空一气之下,拔了一撮脑袋上的猴毛(协程),这些腾空出世的小猴子由他调遣,所以这下佛祖就没辙啦!人一多,办事效率就高了嘛

什么样的协程有意义?

  • 只有遇到io切换才有意义

协程的优点是什么?

  • 应用程序控制切换要比操作系统切换快得多(或者说协程的切换开销更小,属于程序级别的切换,操作系统完全感知不到,更加轻量级)
  • 单线程内就可以实现并发的效果,最大限度地利用CPU

携程的缺点是什么?

  • 协程的本质是在单线程下,无法利用多核,可以是一个程序开启多个进程,每个进程开启多个线程,每个线程开启协程
  • 协程指的是单个线程,因而一旦协程出现阻塞,将会阻塞整个线程

例子

情况一:单纯地切换反而会降低运行效率

import time

def consumer(res):
    '''任务1:接收数据,处理数据'''
    pass

def producer():
    '''任务2:生产数据'''
    res = []
    for i in range(10000000):
        res.append(i)
    return res

start = time.time()

#串行执行
res = producer()
consumer(res) #写成consumer(producer())会降低执行效率
stop = time.time()
print(stoop - start)


#基于yield并发执行
import time 
def consumer():
    '''任务1:接收数据,处理数据'''
    while True:
        x = yield

def producer():
    '''任务2:生产数据'''
    g = consumer()
    next(g)
    for i in range(10000000)
        g.send(i)
        
start = time.time()

#基于yield保存状态,实现两个任务直接来回切换,即并发的效果
#PS:如果每个任务中都加上打印,那么明显地看到两个任务的打印是你一次我一次,即并发执行的

producer()

stop = time.time()
print(stop - start)

情况二:yield不能实现io切换

import time 

def consumer():
    '''任务1:接收数据, 处理数据'''
    while True:
        x = yield
        
def producer():
    '''任务2:生产数据'''
    g = consumer()
    next(g)
    for i in range(10000000):
        g.send(i)
        time.sleep(2)
        
start = time.time()
producer() #并发执行,但是任务producer遇到io就会阻塞住,并不会切到该线程内的其他任务去执行

stop = time.time()
print(stop - start)

gevent模块

介绍

  • 实现并发同步或异步编程

例子

情况一:gevent

遇到io阻塞时会自动切换任务

import gevent

def eat(name):
    print(f'{name} eat 1')
    gevent.sleep(2)
    print(f'{name} eat 2')

def play(name):
    print(f'{name} play 1')
    gevent.sleep(3)
    print(f'{name} play 2')


g1 = gevent.spawn(eat, 'ada')
g2 = gevent.spawn(play, name = 'ada')
g1.join()  #等待g1结束
g2.join()  #等待g2结束
print('主')

情况二:from gevent import money;money.patch_all()

from gevent import monkey;monkey.patch_all()
import gevent
import time

def eat():
    print('eat 1')
    time.sleep(2)
    print('eat 2')

def play():
    print('play 1')
    # 疯狂的计算没有io
    time.sleep(3)
    print('play 2')

start = time.time()
g1 = gevent.spawn(eat)
g2 = gevent.spawn(play)
g1.join()  #等待g1结束
g2.join()  #等待g2结束
end = time.time()
print(end-start)

posted @ 2019-09-19 20:37  fxyadela  阅读(190)  评论(0编辑  收藏  举报