python 并发编程基础之 补充er

1、Event 事件

​ Event的事件作用:
​ 用来控制线程的执行
​ 由一些线程去控制另一些线程

from threading import Thread
from threading import Event
import time

e = Event()  # 调用Event类实例化得到一个对象

def light():
    print('红灯亮......')
    time.sleep(2)
    e.set()   # 调用set方法将其他有e.wait()的线程设置为True,给其开绿灯
    print('绿灯亮了.......')

def car(name):
    print('正在等红灯......')
    e.wait()   # 调用wait方法将本线程设置为 False, 亮红灯,阻塞
    print(f'{name}正在加速冲刺......')

t = Thread(target=light)  # 开启一个控制线程
t.start()

for line in range(10):  # 开启10个受控线程
    t = Thread(target=car, args=(f'{line}号车出发',))
    t.start()

2、进程池与线程池

​ 什么是进程池和线程池:
​ 进程池与线程池是用来控制当前程序允许创建的的进程或线程的数量
​ 进程池和线程池的作用:
​ 保证在硬件允许的范围内创建进程或线程的数量
​ 如何使用:

from concurrent.futures import ProcessPoolExecutor, ThreadPoolExecutor
import time

# ProcessPoolExecutor(5)  # 5表示只能开启5个进程
# ProcessPoolExecutor()  # 表示只能开启和CPU数量一样个数的进程

pool = ThreadPoolExecutor(5)  # 5表示只能开启5个线程
# pool1 = ThreadPoolExecutor()   # 表示只能开启和CPU数量*5 个数的进程

# t = Tread()  # 异步提交
# t.start()

# pool.submit('传函数名')  # 异步提交任务,作用等同于上边两步

# def task():
#     print('线程开始...')
#     time.sleep(1)
#     print('线程结束了。。。')
#
#
# for line in range(5):
#     pool.submit(task)


# pool.submit('传函数名').add_done_callback('回调函数名')
def task(res):
    print('线程开始。。。')
    time.sleep(1)
    print('线程结束。。。')
    return 'ok'


def call_back(res):  # 回调函数
    print(type(res))
    res2 = res.result()  # 注意: 赋值操作不要与接收的res同名
    print(res2)


for line in range(5):
    pool.submit(task, 1).add_done_callback(call_back)

pool.shutdown()  # 会让所有线程池的任务结束后,才往下执行代码
print('OK')

3、协程:

​ 进程: 资源单位

​ 线程: 执行单位

​ 协程: 在单线程下实现并发

注意: 协程不是操作系统资源,他是程序起的名字,目的是让单线程能实现并发.

​ 操作系统:
​ 多道技术, 切换 + 保存状态
1) 遇到IO
2) CPU执行时间过长

​ 协程:
通过手动模拟操作系统 "多道技术",实现 切换 + 保存状态
​ 1)手动实现 遇到IO切换, 欺骗操作系统误以为没有IO操作.

​ 单线程 遇到IO, 切换 + 保存状态

​ 单线程 计算密集型, 来回切换 + 保存状态,反而效率更低

​ 优点:
​ 在IO密集型的情况下, 会提高效率.

​ 缺点:
若在计算密集型的情况下, 来回切换, 反而效率更低

如何实现协程: 切换 + 保存状态

  • yield: 保存状态

gevent:

​ 是一个第三方模块,可以帮你监听IO操作, 并切换.

​ 使用gevent目的: 为了实现单线程下,实现遇到IO, 保存状态 + 切换

from gevent import monkey
monkey.patch_all()  # 可以监听该程序下所有的IO操作
import time
from gevent import spawn, joinall  


def func1():
    print('1')
    # IO操作
    time.sleep(1)


def func2():
    print('2')
    time.sleep(3)


def func3():
    print('3')
    time.sleep(5)


start_time = time.time()

s1 = spawn(func1)  # 用于做切换 + 保存状态
s2 = spawn(func2)  # 用于做切换 + 保存状态
s3 = spawn(func3)  # 用于做切换 + 保存状态

# s2.join()  # 发送信号,相当于等待自己 (在单线程的情况下)
# s1.join()
# s3.join()
# 必须传序列类型(列表和元组)
joinall([s1, s2, s3])

end_time = time.time()

print(end_time - start_time)

TCP服务端实现单线程下实现并发:

server:

import socket
import time
from threading import Thread
from gevent import spawn, monkey

monkey.patch_all()

server = socket.socket()
server.bind(
    ('127.0.0.1', 9528)
)
server.listen(5)
print('启动服务端。。。')

def work(conn):
    while True:
        try:
            data = conn.recv(1024).decode('utf-8')
            if len(data) == 0:
                break
            print(data)
            # time.sleep(1)
            conn.send(data.upper().encode('utf-8'))

        except Exception as e:
            print(e)
            break

    conn.close()

def server2():
    while True:
        conn, addr = server.accept()
        print(addr)
        # t = Thread(target=work, args=(conn, ))
        # t.start()
        spawn(work, conn)


if __name__ == '__main__':
    s1 = spawn(server2)
    s1.join()

client:

import socket
import time
from threading import Thread, current_thread


def client():
    client = socket.socket()
    client.connect(
        ('127.0.0.1', 9528)
    )
    print('启动客户端...')
    number = 0
    while True:
        send_data = f'第{number}个{current_thread().name}发送消息 '
        client.send(send_data.encode('utf-8'))
        data = client.recv(1024)
        print(data.decode('utf-8'))
        number += 1


for i in range(100):
    t = Thread(target=client)
    t.start()

猜你喜欢

转载自www.cnblogs.com/allenchen168/p/11735006.html
今日推荐