并发编程笔记

本文详细介绍了Python中的并发编程概念,包括多道技术、进程调度、进程状态转换、同步异步、阻塞非阻塞、进程创建、数据隔离、进程号、守护进程、消息队列、生产者消费者模式、互斥锁、死锁、递归锁、信号量、线程池、进程池、协程以及TCP的协程化和IO模型。文中通过实例展示了各种并发编程技术的用法和原理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

多道技术

允许多个应用程序同时进入到内存、并且 CPU 交替执行多个代码。

CPU 切换有两种情况:
- 当程序遇到 I/O 操作时,操作系统会剥夺该程序 CPU 的执行权力
- 当一个程序长时间占用 CPU 时,操作系统会剥夺该程序 CPU 的执行权力

进程调度

  • 先来先服务调度算法
  • 短作业优先调度算法
  • 时间片轮转法+多级反馈队列

进程的三状态转换图

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BZwlBq8c-1685849715215)(https://natsume-1316988601.cos.ap-chengdu.myqcloud.com/python_image/CPU%E5%88%87%E6%8D%A2.png)]


同步和异步

用来描述任务的提交方式

  • 同步:任务提交之后原地等待任务的提交结果、期间不做如何事情
  • 异步:任务提交之后不在原地等待任务的结果、而是总结做其他事

阻塞非阻塞

用来描述进程的运行状态

  • 阻塞:阻塞态
  • 非阻塞:就绪态运行态

同步阻塞、同步非阻塞、异步阻塞、异步非阻塞(速度最快)


创建进程

python 创建进程的模块:

'''
os.fork():windows系统支持
multiprocessing
subprocess
'''

multiprocessing

创建进程的第一种方式:

from multiprocessing import Process  
import time  
  
  
def func(name):  
    print(f"{name}进程开始")  
    time.sleep(5)  
    print(f"{name}进程结束")  
  
  
if __name__ == '__main__':  
    p = Process(target=func, args=("小明",))  
    p.start()  
    print("主进程结束")
主进程结束
小明进程开始
小明进程结束

注意一定要加 main()判断!!!要不然代码会变成递归、报错。

创建进程的第二种方式:

class MyProcess(Process):  
    def __init__(self, name):  
        super(MyProcess, self).__init__()  
        self.task_name = name  
  
    def run(self) -> None:  
        print(f"{self.task_name}任务开始")  
        time.sleep(3)  
        print(f"{self.task_name}进程结束")  
  
  
if __name__ == '__main__':  
    p = MyProcess('哈哈哈')  
    p.start()  
    print("主进程结束")
主进程结束
哈哈哈任务开始
哈哈哈进程结束

总结:创建进程就是在内存中申请一块内存空间,然后把需要运行的代码放进去,多个进程的内存空间,它们彼此是隔离的,进程与进程之间的数据,它们是没办法直接交互的如果想要交互,则可以借助第三方工具/模块

join 方法

p.star()
p.json()

注意进程的创建顺序都是随机的、都是由操作系统创建的!

进程间数据隔离

from multiprocessing import Process  
a = 18  
def func():  
    global a  
    a = 10  
  
  
if __name__ == '__main__':  
    p = Process(target=func())  
    p.start()  
    p.join()
    print(a)
10

所以两个进程间的数据是相互隔离的

进程号

Windows:tasklist (查看进程号:pid)
mac/linux:ps aux

代码演示:

from multiprocessing import Process, current_process  
import os  
def func():  
    print(f'进程号为:{current_process().pid}')  
    print(os.getpid())  
    time.sleep(10)  
  
if __name__ == '__main__':  
    p = Process(target=func)  
    p.start()  
  
    print(f"主进程号为:{current_process().pid}")  
    print(os.getpid())
主进程号为:11712
11712
进程号为:18040
18040

所以查看进程号有两种方法:

  • os.getpid:获取当前进程ID
  • os.getppid:获取父进程ID
  • current_process :获取当前进程 ID
p.terminate():杀死当前进程
p.is_alive():判断当前进程是否存在
	-注意:由于代码不能直接杀死进程、只能通过向操作系统请求、所以有一定的延迟,要注意延迟。

僵尸进程、孤儿进程:

  • 僵尸进程
'''
子进程死后,还会有一些资源占用(进程号,进程运行状态,运行时间等),等待父进程通过系统调用回收(收尸)
除了init进程之外,所有的进程,最后都会步入僵尸进程
危害:
	子进程退出后,父进程没有及时处理,僵尸进程就会一直占用计算机资源
'''
  • 孤儿进程
子进程还未结束、父进程就被回收了,之后子进程就会交由系统统一处理。

守护进程

守护进程:父程序死后、子程序也会死
p.daemon = True
p.star()
from multiprocessing import Process  
import time  
  
  
def task(name):  
    print(f'{name} 活着')  
    time.sleep(3)  
    print(f'{name} 死了')  
  
  
if __name__ == '__main__':  
    p = Process(target=task, args=('妲己',))  
    p.daemon = True  
    p.start()  
    time.sleep(1)  
    print("纣王死了,妲己也要死")  
    print('全部结束')
妲己 活着
纣王死了,妲己也要死
全部结束

消息队列


队列:先进先出
管道+锁
堆栈:后进先出

进程间数据相互交互。

queue:

# 两种导入方法
from multiprocessing import Queue 
Queue()

from multiprocessing import Queue 
queue.Queue()

import queue  
queue.Queue()

具体使用方法:

from multiprocessing import Queue  
  
q = Queue(6)  
q.put("a")  
q.put(3)  
  
v1 = q.get()  
v2 = q.get()  
print(v1, v2)
 -1(存数据)当数据超过定义的最大数据量时、程序会阻塞。
 q.put_nowait(value) -如果没位置了直接报错
 q.put(value,  timeout=3) -如果位置满了、且等待了3秒还是满的、报错
-2(取数据)当取得数据量超过了存的数据、阻塞
q.get_nowait(value) -队列没有数据、就报错
q.full() -判断数据是否已满、返回布尔值
q.empty() -判断队列是否为空
可能会不准确。

生产者消费者模式


生产者:产生或制造数据的
消费者:消费或者处理数据的
媒介:消费队列(数据交互的)

from multiprocessing import Queue, Process, JoinableQueue  
import time  
import random  
  
  
def producer(name, food, q):  
    for i in range(8):  
        time.sleep(random.randint(2, 4))  
        q.put(food)  
        print(f"{name} 做了{food}{i}")  
  
  
def consumer(name, q):  
    while True:  
        v = q.get()  
        time.sleep(random.randint(1,3))  
        print(f"{name} 吃了{v}")  
  
  
        q.task_done()  # 告诉队列、我们已经拿走了一个数据、并且已经处理完了  
  
'''  
JoinableQueue  
在Queue的基础上多了一个计数器机制,每put一个数据,计数器就加一  
每调用一次task_done,计数器就减  
当计数器为0的时候,就会走q.join后面的代码  
'''  
  
if __name__ == '__main__':  
    q = JoinableQueue()  
    p1 = Process(target=producer, args=("小当家", "黄金炒饭", q))  
    p2 = Process(target=producer, args=("神厨小福贵", "佛跳墙", q))  
    c1 = Process(target=consumer, args=("八戒", q))  
  
    c1.daemon = True # 设置守护进程、主进程死后、子进程也要死、而不是等待。  
  
    p1.start()  
    p2.start()  
    c1.start()  
  
    p1.join() # 必须加  
    p2.join() # 必须加(保证每个数据全部处理完)  
  
    q.join()  
  
    # 主进程死了、子进程也要陪葬。(设置守护进程)

互斥锁

当多个进程同时操作一个数据时、会发生数据错乱的情况、解决的方法就是给数据加

并发变串行,牺牲效率、提高安全。

import random  
from multiprocessing import Process, Lock  
import json  
import time  
  
  
def search_tickets(name):  
    with open('date/test03.json', 'r', encoding='utf-8') as f:  
        count = json.load(f)  
        print(f'{name}执行查询操作,余票为:{count["count"]}')  
        return count  
  
  
def buy_tickets(name):  
    with open('date/test03.json', 'r', encoding='utf-8') as f:  
        count = json.load(f)  
        time.sleep(random.randint(1, 5))  
        if count.get('count') > 0:  
            count['count'] -= 1  
            with open('date/test03.json', 'w', encoding='utf-8') as f:  
                json.dump(count, f)  
            print(f'用户:{name},抢票成功')  
        else:  
            print(f'{name} 余票不足抢票失败')  
  
  
def task(name, mutex):  
    search_tickets(name)  
    # 加锁  
    mutex.acquire()  
    buy_tickets(name)  
    # 释放锁  
    mutex.release()  
  
  
if __name__ == '__main__':  
    mutex = Lock()  
    for i in range(1, 9):  
        p = Process(target=task, args=(' ', mutex))  
        p.start()
 执行查询操作,余票为:4
 执行查询操作,余票为:4
 执行查询操作,余票为:4
 执行查询操作,余票为:4
 执行查询操作,余票为:4
 执行查询操作,余票为:4
 执行查询操作,余票为:4
 执行查询操作,余票为:4
用户: ,抢票成功
用户: ,抢票成功
用户: ,抢票成功
用户: ,抢票成功
  余票不足抢票失败
  余票不足抢票失败
  余票不足抢票失败
  余票不足抢票失败

多线程、多进程对比:

在计算密集(多进程)和、IO 密集(多线程)对比:

os.cpu_count()-查看当前电脑CPU数量(几核)
from threading import Thread  
from multiprocessing import Process  
  
import time  
  
  
def task():  
    res = 0  
    for i in range(100000000):  
        res += 1  
  
  
if __name__ == '__main__':  
    l = []  
    ps = time.time()  
    for i in range(8):  
        # p = Process(target=task) # 16.27019715309143  
        p = Thread(target=task) # 31.66116213798523  
        p.start()  
        l.append(p)  
    for p in l:  
        p.join()  
    pe = time.time()  
    print(pe - ps)

总结:

  • 多进程和多线程都有各自的优势、以后写代码可以多个进程下、写多个线程。
  • 现在开发的程序、多数都是 IO 密集型

死锁

死锁现象基本是锁的乱用、加上并发造成的(抢锁和释放锁矛盾冲突)

import time  
from threading import Thread, Lock, current_thread  
from multiprocessing import Process  

mutex1 = Lock()  
mutex2 = Lock()  
  
  
def test():  
    mutex1.acquire()  
    print(f"{current_thread().name} 抢到了锁1")  
    mutex2.acquire()  
    print(f"{current_thread().name} 抢到了锁2")  
    mutex2.release()  
    mutex1.release()  
  
    mutex2.acquire()  
    print(f"{current_thread().name} 抢到了锁1")  
    time.sleep(1) # 为了更直接展示  
    mutex1.acquire()  
    print(f"{current_thread().name} 抢到了锁2")  
    mutex1.release()  
    mutex2.release()  
  
if __name__ == '__main__':  
    for i in range(10):  
        t = Thread(target=test)  
        t.start()
Thread-1 抢到了锁1
Thread-1 抢到了锁2
Thread-1 抢到了锁1
Thread-2 抢到了锁1

程序一直卡住了

递归锁 :RLock

内部有一个计数器、每 acquire 一次计数器就会+1,每 release 一次、计数器就会-1。只要计数器不为 0,其他人就不能抢这把锁。

可递归、不会阻塞。而 Lock 不能重复抢锁。

信号量


在不同的阶段、可能会对应不同的技术点,对于并发编程来说、它指的是“锁”。 它可以用来控制同时访问特定资源的线程数量,通常用于某些资源有明确访问数量的场景简单来说就是用于**限流** ```python from threading import Thread, Semaphore import time import random

sp = Semaphore(5)

def task(name):
sp.acquire()
print(name, “抢到了锁”)
time.sleep(random.randint(3,5))
sp.release()

if name == ‘main’:
for i in range(25):
t = Thread(target=task, args=(f"bike{i+1}号",))
t.start()

## Event 事件
> 子进程/线程等待子进程/线程(正常情况下)

```python
from threading import Thread, Event  
import time  
  
even = Event()  
  
  
def bus():  
    print("公交车即将到站")  
    time.sleep(3)  
    print("公交车到站了")  
    even.set()  # 发射了一个信号、车来了赶快上车  
    print()  
  
  
def passenger(name):  
    print(name, "正在等车")  
    even.wait()  # 等待公交车来  
    print(name, "上车了")  
  
  
if __name__ == '__main__':  
    t = Thread(target=bus())  
    t.start()  
    for i in range(10):  
        pa = Thread(target=passenger, args=(f"person{i + 1}",))  
        pa.start()


池是用来保护计算机硬件安全的情况下、最大限制的利用计算机资源、降低了程序的运行效率、但保证了计算机硬件的安全。

主要应用:

  • 线程池:ThreadPoolExcutor
  • 进程池:ProcessPoolExcutor
from concurrent.futures import ThreadPoolExecutor, ProcessPoolExecutor

使用:

from concurrent.futures import ThreadPoolExecutor, ProcessPoolExecutor  
import time  
  
pool = ThreadPoolExecutor(10)  # 默认有值  
  
  
def task(name):  
    print(name)  
    time.sleep(3)  
  
for i in range(50):  
    pool.submit(task, i)

image.png

函数会按照要求创建线程
-线程池:线程是自动创建的、用于控制线程的数量。
-信号量(锁):线程自己创建、控制程序的执行、阻塞。

异步获取提交的数据:

from concurrent.futures import ThreadPoolExecutor, ProcessPoolExecutor  
import time  
  
pool = ThreadPoolExecutor()  # 默认有值  
  
  
def task(name):  
    print(name)  
    time.sleep(3)  
    return name +10  
f_list = []  
for i in range(50):  
    p = pool.submit(task, i)  
    f_list.append(p)  
  
  
pool.shutdown()  # 关闭线程池、等待线程池中所有的任务全都运行完毕 (类似于线程的join()方法)  
  
  
for i in f_list:  
    print(i.result())
0
1
2
3
4
...

数据会先全部提交完毕后、代码才会继续往下走、然后执行获取返回结果的代码

对于进程池、进程池的进程是不会替换的、在不断的循环往复中、进程号都不会变、还是那几个进程。

异步回调机制


from concurrent.futures import ThreadPoolExecutor, ProcessPoolExecutor  
import time  
  
pool = ProcessPoolExecutor()  # 默认有值  
  
  
def task(name):  
    print(name)  
    time.sleep(3)  
    return name + 10  
  
  
def call_back(res):  
    print("call_back", res.result())  
  
  
  
if __name__ == '__main__':  
    f_list = []  
    for i in range(50):  
        pool.submit(task, i).add_done_callback(call_back)

主要区别:.add_done_callback(call_back) 增加了异步回调函数、使得当任务提交运行完后、立刻就能拿到结果。

0
1
2
3
4
5
6
7
8
call_back 10
9
10
call_back 11
call_back 12

协程


也可以称为微线程、它是一种用户态内的上下文切换技术:单线程下实现并发效果

进程:资源单位
线程:执行单位
协程:认为构造出来的:(切换+保存状态)

注意:切换不一定提示效率!!
#yield
[[yield]]

def f1():  
    n = 0  
    for i in  range(100000000):  
        n +=1  
        yield  
  
def f2():  
    g = f1()  
    n = 0  
    for i in  range(100000000):  
        n +=1  
        next(g)  
start  = time.time()  
f2()  
end = time.time()  
print(end - start)  
  
# 17.651402950286865

gevent


:遇到 IO 就切换

pip install gevent
from gevent import monkey  # 打补丁  
  
monkey.patch_all()  # 检测所有的IO操作  
from gevent import spawn  
import time  
  
  
def da():  
    for i in range(3):  
        print('da')  
        time.sleep(2)  
  
  
def ma():  
    for i in range(3):  
        print('damai')  
        time.sleep(2)  
  
  
def buyao():  
    for i in range(3):  
        print('不要')  
        time.sleep(3)  
  
  
s = time.time()  
g1 = spawn(da)  
g2 = spawn(ma)  
g3 = spawn(buyao)  
  
g1.join()  # 加时间等待  
g2.join()  # 加等待  
g3.join()  
  
en = time.time()  
print(en - s)
da
damai
不要
da
damai
不要
da
damai
不要
9.072951316833496

TCP 协程化


# server
from gevent import monkey  
monkey.patch_all()  
from  gevent import spawn  
import socket  
  
def comm(conn):  
    while True:  
        try:  
            data = conn.recv(1024)  
            if not data:  
                break  
            conn.send(data.upper())  
        except:  
            break  
    conn.close()  
  
  
def run(ip, port):  
    server = socket.socket()  # 默认tcp协议  
    server.bind((ip, port))  
    server.listen(5)  
    while True:  
        conn, addr = server.accept()  
        spawn(comm,conn)  
  
if __name__ == '__main__':  
    # run('127.0.0.1', 8080)  
    g = spawn(run, '127.0.0.1', 8002)  
    g.join()
# client
 
import socket  
from threading import Thread, current_thread  
  
def t_client():  
    client = socket.socket()  
    client.connect(('127.0.0.1', 8002))  
  
    n = 0  
    while True:  
        msg = f"{current_thread().name} say {n}"  
        client.send(msg.encode('utf-8'))  
        data = client.recv(1024)  
        print(data.decode('utf-8'))  
        n += 1  
  
if __name__ == '__main__':  
    for _ in range(100):  
        Thread(target=t_client).start()

在服务端哪里、采用 spawn 改写、客户端用多线程模拟 1000 人同时访问,可承受

IO 模型


主要研究的是网络 IO

  • 等待数据准备(waiting for the data to ready)
  • 把数据从内核拷贝到进程(copy data)
    网络 IO
  • accept
  • recv
  • send

阻塞 IO


非阻塞 IO


# server
import socket  
  
server = socket.socket()  
server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)  
server.bind(('127.0.0.1', 8990))  
server.listen(5)  
server.setblocking(False)  # 将所有的阻塞变为非阻塞  
  
c_list = []  
d_list = []  
while True:  
    try:  
        conn, addr = server.accept()  
        c_list.append(conn)  
    except BlockingIOError:  
        for conn in c_list:  
            try:  
                data = conn.recv(1024)  
                if not data:  
                    conn.close()  
                    d_list.append(conn)  
                conn.send(data.upper())  
            except BlockingIOError:  
                pass  
            except ConnectionResetError:  
                conn.close()  
                d_list.append(conn)  
        for conn in  d_list:  
            c_list.remove(conn)  
        d_list.clear()

当然以上代码只是非阻塞 IO 的思路,不必较真。

  • 太浪费计算机资源

IO 多路复用


import select  
import socket  
  
server = socket.socket()  
server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)  
server.bind(('127.0.0.1', 8080))  
server.listen(5)  
server.setblocking(False)  
  
input_server = [server]  
while True:  
    rlist, wlist, xlist = select.select(input_server, [], [])  
    for i in rlist:  
        if i is server:  
            conn, addr = i.accept()  
            input_server.append(conn)  
            continue  
        try:  
            data = i.recv(1024)  
            if not data:  
                i.close()  
                input_server.remove(i)  
                continue  
            i.send(data.upper())  
        except ConnectionResetError:  
            i.close()  
            input_server.remove(i)  
            continue

监管机制:

  • select (linux 和 Windows 都有)
  • poll (只有 linux 有、且监管数量比 select 多的多)

缺点:由于监管过程类似与 for 循环的效果、所以在对象特别多的时候、延迟有点大

  • epoll (linux)

它给每一个监管对象都绑定了一个回调机制,一旦有响应回调机制就会把可读对象放入就绪链表,epo‖只需要判断就绪链表是否为空,不需要每次都把所有的监管对象都遍历一遍,节省了 cpu 大量的时间,性能也得到了大幅度提升

selectors:自适应代码


因为每个管理机制都有各自的优势、所以为了实现不同服务端的高效并发性、大佬写了 selectors 库、自动根据终端选择合适的管理机制,做自动化代码。

# server
# coding : utf-8  
# 夏目&青一  
# @name:13-selectors服务端  
# @time: 2023/5/24  23:10  
  
import socket  
import selectors  
  
  
def accept(server):  
    conn, addr = server.accept()  
    sel.register(conn, selectors.EVENT_READ, read)  
  
  
def read(conn):  
    try:  # windows 版本兼容  
        data = conn.recv(1024)  
        if not data:  
            conn.close()  
            sel.unregister(conn)  
            return  
        conn.send(data.upper())  
    except ConnectionResetError:  
        conn.close()  
        sel.unregister(conn)  
        return  
  
  
server = socket.socket()  
server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)  
server.bind(('127.0.0.1', 8081))  
server.listen(5)  
server.setsockopt(False)  
  
sel = selectors.DefaultSelector()  
sel.register(server, selectors.EVENT_READ, accept)  # 2设置可读监控  3回调函数  
  
while True:  
    events = sel.select()  
    for key, mask in events:  
        callback = key.data  
        callback(key.fileobj)

异步 IO


实现库

  • asyncio (python 3.4)
  • tornado
  • fastapi
  • django 3
  • sanic
  • vibora
  • quart
  • twisted
  • aiohttp

异步编程的基本流程

简化版:

image.png

await 后面只能跟一种对象:可等待对象(
协程对象、
task 对象、
future 对象

# 3.7之后
import asyncio  
import time  
from threading import current_thread  
  
  
def recv():  
    print("开始")  
    asyncio.sleep(2)  
    print("结束")  
  
  
async def f1():  
    print(f'任务:f1 开始 {current_thread().name}')  
    data = await recv()  
    print(data)  
    print(f'任务:f1 结束 {current_thread().name}')  
  
  
async def f2():  
    print(f'任务:f1 开始 {current_thread().name}')  
    await recv()  
    print(f'任务:f1 结束 {current_thread().name}')  
  
  
tasks = [f2(), f2()]  
  
asyncio.run(asyncio.wait(tasks))

之前的写法介绍可参考:
async await
官网地址

async def cancel_me():
    print('cancel_me(): before sleep')

    try:
        # Wait for 1 hour
        await asyncio.sleep(3600)
    except asyncio.CancelledError:
        print('cancel_me(): cancel sleep')
        raise
    finally:
        print('cancel_me(): after sleep')

async def main():
    # Create a "cancel_me" Task
    task = asyncio.create_task(cancel_me())

    # Wait for 1 second
    await asyncio.sleep(1)

    task.cancel()
    try:
        await task
    except asyncio.CancelledError:
        print("main(): cancel_me is cancelled now")

asyncio.run(main())

异步迭代器:

迭代器:内置__iter__、next

# 普通的  
class MyRange(object):  
    def __init__(self, start, end=None):  
        if end:  
            self.count = start - 1  
            self.end = end  
        else:  
            self.count = -1  
            self.end = start  
  
    def add_count(self):  
        self.count += 1  
        if self.count == self.end:  
            return None  
        return self.count  
  
    def __iter__(self):  
        return self  
  
    def __next__(self):  
        value = self.add_count()  
        if value is None:  
            raise StopIteration  
        return value  
  
  
# 测试迭代器  
for i in MyRange(10):  
    print(i)

# 输出1、2、3、4、、、

非协程改写

非协程函数需要用到协程的改写

def f1(x):  
    time.sleep(3)  
    return 'hellow'  
  
async def main():  
    loop = asyncio.get_running_loop()  
    future = loop.run_in_executor(None,f1,'values')  
    res = await future  
    print(res)  
  
asyncio.run(main())

把本不支持的函数、放到线程池里面、从而实现异步

异步上下文管理器

上下文管理器:
with open()
	- 对象内部需要定义__enter__方法、__exit__方法
import socket  
  
class Client(object):  
    def __init__(self, ip, port):  
        self.ip = ip  
        self.port = port  
  
    def __enter__(self):  
        self.c = socket.socket()  
        self.c.connect((self.ip,self.port))  
        return self  
    def __exit__(self, exc_type, exc_val, exc_tb):  
        self.c.close()  
    def recv(self):  
        pass  
  
# 测试  
with Client("127.0.0.1", 8080) as c:  
    c.recv()

就可以用支持 with 语法了

客户端协程代码(上下文形式)

class Client(object):  
    def __init__(self, ip, port):  
        self.ip = ip  
        self.port = port  
        self.loop = asyncio.get_running_loop()  
  
    async def __aenter__(self):  
        self.c = socket.socket()  
        # self.c.connect((self.ip,self.port))  
        # 异步链接服务器  
        await self.loop.sock_connect(self.c, (self.ip, self.port))  
        return self  
  
    async def __aexit__(self, exc_type, exc_val, exc_tb):  
        self.c.close()  
  
    async def recv(self):  
        data = await self.loop.sock_recv(self.c, 1024)  
        return data  
  
    async def send(self, data):  
        await self.loop.sock_sendall(self.c, data.encode('utf-8'))  
  
  
# 测试  
async def main():  
    async with Client('127.0.0.1', 8090) as c:  
        await c.send('abc')  
        data = await c.recv()  
        print(data)  
  
  
asyncio.run(main())

服务端

import socket  
import asyncio  
  
  
async def waiter(conn, loop):  
    while True:  
        try:  
            data = await loop.sock_recv(conn, 1024)  
            if not data:  
                break  
            await loop.sock_sendall(conn, data.upper())  
        except ConnectionResetError:  
            break  
    conn.close()  
  
  
  
  
async def main(ip, port):  
    server = socket.socket()  
    server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)  
    server.bind((ip, port))  
    server.listen(5)  
    server.setblocking(False)  
    loop = asyncio.get_running_loop()  
    while True:  
        conn, addr = await loop.sock_accept(server)  
        # 创建事件循环  
        loop.create_task(waiter(conn, loop))  
  
  
asyncio.run(main('localhost',8090))

uvloop

通过改变 asyncio 时间循环默认的源、从而提高协程效率(两倍差不多)

import uvLoop
# 打补丁
asyncio.set_event_loop_policy(uvloop.EventLoopPolicy())

学习笔记:

小飞有点东西

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值