ディレクトリ
並行プログラミングの概要(2)
セマフォ
- セマフォは実際にはロックです。
- 同時にミューテックス一つだけのスレッドが使用するように、セマフォは、複数のスレッドを可能にします。セマフォがインスタンス化されるとき、それは、同時にスレッドの数を定義するパラメータを渡すことができ
- あなたは、ガイドを必要とします
from threading import Semaphore
例:
from threading import Semaphore, Lock
from threading import current_thread
from threading import Thread
import time
sm = Semaphore(5) #
mutex = Lock() #
def task():
# mutex.acquire()
sm.acquire()
print(f'{current_thread().name}执行任务')
time.sleep(1)
sm.release()
# mutex.release()
for line in range(20):
t = Thread(target=task)
t.start()
# 结果:
Thread-1执行任务
Thread-2执行任务
Thread-3执行任务
Thread-4执行任务
Thread-5执行任务
此处等待1秒 # 就是每个线程里的那个 time.sleep(1)
Thread-6执行任务
Thread-9执行任务
Thread-7执行任务
Thread-8执行任务
Thread-10执行任务
此处等待1秒
Thread-11执行任务
Thread-14执行任务
Thread-12执行任务
Thread-13执行任务
Thread-15执行任务
此处等待1秒
Thread-16执行任务
Thread-18执行任务
Thread-17执行任务
Thread-19执行任务
Thread-20执行任务
此处等待1秒
二、イベント
1.イベントとは何ですか
- これは、スレッドの方法です。スレッドが閉塞してブロック解除を制御するために使用します。
2.イベントの使用は何ですか
- スレッドの実行を制御するために使用される、スレッドは、実行の他のスレッドの数によって制御されます。
3.イベントメソッド
wait
このようなコードがスレッドを存在する場合、あなたはそれが永久的なブロッキング状態になりますが発生します。set
このようなコードがスレッドを存在する場合、それはすべて満たしますwait
オープン。レディ状態にそのような元のスレッド。
例:
def light():
print('红灯亮...')
time.sleep(5)
# 应该开始发送信号,告诉其他线程准备执行
e.set() # 将car中的False ---> True
print('绿灯亮...')
def car(name):
print('正在等红灯....')
# 让所有汽车任务进入阻塞态
e.wait() # False
print(f'{name}正在加速漂移....')
# 让一个light线程任务 控制多个car线程任务
t = Thread(target=light)
t.start()
for line in range(10):
t = Thread(target=car, args=(f'童子军jason{line}号', ))
t.start()
第三に、プロセス・スレッド・プールとプール
1.プロセス・スレッド・プールとプールとは何ですか
- プロセスプールとスレッドプールは現在のプログラムを制御するために使用されるプロセスは数/スレッドを作成することを可能にします。
使用何2.プロセスプールとスレッドプール
- 数は、ハードウェア・プロセス/スレッドによって許容される範囲内にあることを確実にするために作成
3.
ライブラリガイド:
from concurrent.futures import ProcessPoolExecutor
細胞プロセス/スレッド・プール・オブジェクトの例:
pool = ThreadPoolExecutor(n)
、nは充填する時間から充填することができ、n個の充填口は、プロセス/スレッドを表し、CPU番号を記入していないCPUの数を制限するためにデフォルトの制限をプロセスの数、デフォルトのスレッド数を示し、* 5submit
アドレス(すなわち、関数名)の関数にパラメータを渡し、非同期タスクを提出することです。submit(任务函数地址).add_done_callback(回调函数的地址)
。ときにタスク関数がコールバック関数によって受信された値を返します。このような形状としてコールバックパラメータres
、res
しない関数の戻り値のタスク。することでres.result()
、タスクを取得するための関数の戻り値。shutdown
結局それはそれは、ダウンプロセス/スレッドのようなコードを実行し、スレッドプールの作業を行いますjoin
効果
例:
# 任务函数没有返回值时*************************
def task():
print('线程任务开始了...')
time.sleep(3)
print('线程任务结束了...')
for line in range(5):
pool.submit(task)
pool.shutdown()
print('hello')
# 任务函数有返回值时**********************************
def task(res):
# res == 1
print('线程任务开始了...')
time.sleep(1)
print('线程任务结束了...')
return 123
# 回调函数
def call_back(res):
print(type(res))
# 注意: 赋值操作不要与接收的res同名
res2 = res.result()
print(res2)
for line in range(5):
pool.submit(task, 1).add_done_callback(call_back)
pool.shutdown()
print('hello')
第四に、コルーチン(使用のみ1つのスレッド)
1.プロセス/スレッド/コルーチン差
- プロセス:リソースユニット
- スレッド:実装ユニット
- コルーチン:シングルスレッドの同時実行で
- 注意:コルーチンないオペレーティングシステムの概念は、それが人為的にシングルスレッドの同時実行を可能にするために、生成された名前です。
2.コルーチンの効果を
- マルチチャネル技術は、コアである:保存+のスイッチング状態
- 同時に、メモリ、マルチチャネルタスクを置きます
- マルチチャンネルタスクがCPU上で実行するターン、IOや経験CPU時間がスイッチに長すぎます
- 切り替え時の作業進行状況が保存されます
コルーチン
+アナログシステムの手動操作によって保存された状態を切り替え実現、「マルチチャネル技術」
CPU、マルチプロセス/スレッドとして複数のタスクとしてスレッドをしてみましょう。
- 状態を保存する+複数のタスク間を行き来するスレッドの切り替えをすることです
- 集中的なIOの場合は、状態を保存+ IOスイッチを満たします
計算集約のために、トグル+は状態を保存します。計算集約のために、より低い効率コルーチンの使用。
利点:
IO集中型の場合には、効率を改善します
短所:
計算集約の場合には、前後に切り替えるので、あまり効率的で
3.コルーチンを実装する方法
(1)ここで、計算集約
- 使用
yield
状態を保存します - 切り替えはと同時であります
例:
import time
def func1():
while True:
10000000+1
yield
def func2():
g = func1() # 启动生成器 ,g 为生成器对象
for i in range(10000000):
time.sleep(10) # 模拟IO,yield并不会捕捉到并自动切换
i+1
next(g)
start = time.time()
func2()
stop = time.time()
print(stop-start)
(2)IO集中ケース
サードパーティ製のライブラリをインポートするには:
from gevent import monkey,spawn , joinall
gevent
それは、私たちはIO操作とスイッチを監視するのに役立ち、サードパーティのライブラリです使用
gevent
目的は、単一のスレッドがIO、保存+のスイッチング状態が発生し得ることですmonkey.patch_all()
すべてのIOは、リスナーの下で動作することができますspawn 和 joinall
保存状態のスイッチが実装されてもよいです+
例:
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)
第五に、シングルスレッドの同時実行サーバーを達成するために
例:
# 客户端*************************************
import socket
import time
from threading import Thread, current_thread
def client():
client = socket.socket()
client.connect(
('127.0.0.1', 9527)
)
print('启动客户端...')
number = 0
while True:
send_data = f'{current_thread().name} {number}'
client.send(send_data.encode('utf-8'))
data = client.recv(1024)
print(data.decode('utf-8'))
number += 1
# 模拟了300个用户并发去访问服务端
for i in range(300):
t = Thread(target=client)
t.start()
# 服务端*********************************
from gevent import monkey
monkey.patch_all() # 检测IO
import socket
import time
from threading import Thread
from gevent import spawn
server = socket.socket()
server.bind(
('127.0.0.1', 9527)
)
server.listen(5)
print('启动服务端...')
# 线程任务,执行接收客户端消息与发送消息给客户端
def working(conn):
while True:
try:
data = conn.recv(1024).decode('utf-8')
if len(data) == 0:
break
print(data)
# time.sleep(1)
send_data = data.upper().encode('utf-8')
conn.send(send_data)
except Exception as e:
print(e)
break
conn.close()
def server2():
while True:
conn, addr = server.accept()
# print(addr)
# t = Thread(target=working, args=(conn,))
# t.start()
spawn(working, conn)
if __name__ == '__main__':
s1 = spawn(server2)
s1.join()
並行プログラミングモジュールで遭遇六
モジュール及び方法は、1プロセスをインポートする必要があります
from multiprocessing import Process , Lock , RLock , Queue , Semaphore , Event
# 分别用来:创建进程,创建互斥锁,创建递归锁,创建队列,创建信号量 ,创建事件
モジュールおよび方法は、スレッド2をインポートする必要があります
from threading import Thread , current_thread , Lock , RLock , queue , Semaphpre , Event
# 分别用来:创建线程,返回当前线程的信息 , 创建互斥锁,创建递归锁,创建队列,创建信号量 ,创建事件
3.プール、プール内のプロセスモジュールとスレッドの方法をインポートします
from concurrent.futures import ProcessPoolExecutor, ThreadPoolExecutor
# 分别用来: 创建进程池 , 创建线程池
モジュールと4コルーチンをインポートする方法
from gevent import monkey , spawn , joinall
# 分别用来 监听线程中的所有IO操作 , 调用定义的任务 , 让主线程等待所有调用的任务执行结束再结束