コルーチン
コルーチン:マイクロスレッド、ファイバーとも呼ばれるシングルスレッドでの同時実行。英語名はコルーチンです。
一文でスレッドとは何かを説明します。コルーチンはユーザーモードの軽量スレッドです。つまり、コルーチンはユーザープログラム自体によって制御およびスケジュールされます。
- Pythonスレッドはカーネルレベルに属します。つまり、オペレーティングシステムがスケジューリングを制御します(単一のスレッドがioに遭遇したり、実行時間が長すぎるなど、CPU実行権限を引き渡したり、他のスレッドを切り替えて実行したりする必要があります) )
- コルーチンはシングルスレッドで開始され、ioが検出されると、効率を向上させるために(オペレーティングシステムではなく)アプリケーションレベルから切り替えが制御されます(!!!非io操作の切り替えは効率とは関係ありません) )
オペレーティングシステムの制御スレッドスイッチと比較して、ユーザーはコルーチンのスイッチを単一のスレッドで制御
します。利点は次のとおりです。
- コルーチンのスイッチングオーバーヘッドは小さく、これはプログラムレベルのスイッチングに属し、オペレーティングシステムはコルーチンに完全に影響されないため、より軽量です。
- 並行性の効果は単一のスレッドで達成でき、CPUは最大限に使用できます
欠点は次のとおりです。
- コルーチンの本質は、シングルスレッドであり、複数のコアを使用できないことです.1つのプログラムが複数のプロセスを開き、各プロセスが複数のスレッドを開き、各スレッドがコルーチンを開く可能性があります
- コルーチンは単一のスレッドを参照するため、コルーチンがブロックされると、スレッド全体がブロックされます
コルーチンの特性の要約:
同時実行は単一のスレッドでのみ実装する
必要があります。共有データをロックする必要はありません。
ユーザープログラムは複数の制御フローのコンテキストスタックを保存します。
追加:コルーチンは、遭遇すると自動的に他のコルーチンに切り替わります。IO操作(検出IOを達成するには、yieldとgreenletを達成できないため、geventモジュール(選択メカニズム)を使用します)
信号
信号量代码演示(信号量指的是同一时间有多把锁并发运行)
Semaphore 导入信号量模块
from threading import Thread, Semaphore, current_thread
import time
def func():
sm.acquire() # 抢锁
print("%s 干饭 " % current_thread().getName())
time.sleep(3)
sm.release() # 释放锁
if __name__ == '__main__':
sm = Semaphore(5) # 一共有5把锁
for i in range(21):
t = Thread(target=func)
t.start()
デッドロック現象と再帰ロック
デッドロック現象が発生してもエラープロンプトは表示されず、ロックスリーブロックがデッドロック現象として表示されます。
模拟死锁代码演示
from threading import Thread, Lock
mutexA = Lock()
mutexB = Lock()
class Mythread(Thread):
def __init__(self, name):
super().__init__()
self.name = name
def run(self):
self.f1()
self.f2()
def f1(self):
mutexA.acquire()
print("%s 抢到了A锁" % self.name) # 线程1先抢到了a锁,继续抢了b锁。这时候其他三个线程都并发抢a锁,
# 线程1释放了b锁,再释放了a锁,其他三个并发抢a锁的同时,线程1又抢了b锁并且睡了0.1秒
mutexB.acquire() # 这个时候b锁被线程2抢到了,a锁在线程1的手里。线程1和线程2互相锁死。线程3和线程4还在等着抢a锁
print("%s 抢到了B锁" % self.name)
mutexB.release()
mutexA.release()
def f2(self):
mutexB.acquire()
print("%s 抢到了B锁" % self.name)
time.sleep(0.1)
mutexA.acquire()
print("%s 抢到了A锁" % self.name)
mutexA.release()
mutexB.release()
if __name__ == '__main__':
t1 = Mythread("线程1")
t2 = Mythread("线程2")
t3 = Mythread("线程3")
t4 = Mythread("线程4")
t1.start()
t2.start()
t3.start()
t4.start()
print("主线程")
死锁现象发生打印的结果:
线程1 抢到了A锁
线程1 抢到了B锁
线程1 抢到了B锁
线程2 抢到了A锁
主线程
再帰ロック
递归锁同一把锁可以连续acquire,以计数的方式来抢锁和释放锁
from threading import Thread, RLock
mutexA = mutexB = RLock() # 递归锁,一个进程可以连续抢锁,抢一次锁技计数1次,第一次计数1,第二次计数2。。。。
# 其他的线程如果发现递归锁的计数不为0就直接堵塞在原地,等这把锁计数为0的时候才能抢锁
class Mythread(Thread):
def __init__(self, name):
super().__init__()
self.name = name
def run(self):
self.f1()
self.f2()
def f1(self):
mutexA.acquire()
print("%s 抢到了A锁" % self.name)
mutexB.acquire()
print("%s 抢到了B锁" % self.name)
mutexB.release()
mutexA.release()
def f2(self):
mutexB.acquire()
print("%s 抢到了B锁" % self.name)
time.sleep(0.1)
mutexA.acquire()
print("%s 抢到了A锁" % self.name)
mutexA.release()
mutexB.release()
if __name__ == '__main__':
t1 = Mythread("线程1")
t2 = Mythread("线程2")
t3 = Mythread("线程3")
t4 = Mythread("线程4")
t1.start()
t2.start()
t3.start()
t4.start()
print("主线程")
イベントイベント
1つのスレッドが終了すると、別のスレッドが実行を開始します。データの転送は含まれません。別のスレッドに通知するためのシグナル(データ)を送信します。
代码演示
from threading import Event, Thread, current_thread
e = Event() # 默认全局变量为False
def f1():
print("%s 运行" % current_thread().name) # current_thread().name获取线程名字
time.sleep(3)
e.set() # 把全局变量变成了True
# e.clear() # 把全局变量变成False
# e.is_set() # 判断有没有设置过,设置过就是True,没有设置过就是False
def f2():
e.wait() # 在原地等待,等到全局变量变为True。e.wait(n)可以设置等待时间,超时如果全局变量还没有变成True那么会自动执行代码
print("%s 运行" % current_thread().name)
if __name__ == '__main__':
t1 = Thread(target=f1)
t2 = Thread(target=f2)
t1.start()
t2.start()
ケーススタディ
from threading import Event, Thread, current_thread
import time
import random
e = Event() # e = Ture
def task1():
while True:
e.clear() # e = False
print("红灯亮")
time.sleep(2)
e.set() # e = True
print("绿灯亮")
time.sleep(3)
# task1:2秒亮一次红灯,三秒亮一次绿灯。不停的在循环
def task2():
while True:
if e.is_set(): # 判断e有没有被设置过,如果设置过就是True,没有设置过就是False
print("%s 走你" % current_thread().name)
break # 线程只有进入“走你”才会结束循环
else:
print("%s 等灯" % current_thread().name)
e.wait() # 等待全局变量变为True
if __name__ == '__main__':
Thread(target=task1).start()
while True:
time.sleep(random.randint(1, 5)) # 每1-5秒之间产生循环
Thread(target=task2).start()
タイマーとスレッドキュー
タイマー
from threading import Timer
def hello(x):
print("hello,world", x)
t = Timer(0.2, hello, args=(10,)) # Timer(n,"xx")可以在定时器第一个逗号前面写上时间,表示过多长时间运行代码
t.start()
スレッドキュー(キュー、スタック、優先キューがあります)
import queue
队列(先进先出)
q = queue.Queue(3)
q.put(111)
q.put("aaa")
q.put((1, 2, 3))
print(q.get())
print(q.get())
print(q.get())
堆栈(后进先出)
q = queue.LifoQueue(3)
q.put(111)
q.put("aaa")
q.put((1, 2, 3))
print(q.get())
print(q.get())
print(q.get())
优先级队列(元组,第一个整型数字是优先级排序)
q = queue.PriorityQueue(3)
q.put((2, (111)))
q.put((3, "aaa"))
q.put((1, (1, 2, 3)))
print(q.get())
print(q.get())
print(q.get())
GILインタープリターロック(cpythonインタープリターに付属するロック)
Pythonインタープリターでは、同じプロセスで開かれた複数のスレッドは、同時に1つのスレッドでのみ実行でき、マルチコアを利用できません。Pythonスレッドは次のことを行う
必要があります。インタプリタのギルを取得するロックは、インタプリタの実行権限に相当するコードを実行できます。実行権限があれば、オペレーティングシステムはcpuをPythonコードに割り当てます。cpuが転送されると、ギルロックは直接
主にPythonインタープリターを保護するためにgilロックを解放するメモリ管理のデータセキュリティは、自分で作成したpythonコードのデータセキュリティを保護できません
。gilロックはスレッドを並行させることはできますが、スレッドを並列にすることはできません(pythonは使用することしかできません)プロセスを実行するための1つのcpu、同時に複数のcpusを使用する場合コンピューティング効率を向上させるには、複数のプロセスを開く必要があります)
from threading import Thread, Lock
import time
mutex = Lock()
n = 100
def task():
global n
mutex.acquire() # 互斥锁
temp = n
time.sleep(0.1)
n = temp - 1
mutex.release()
if __name__ == '__main__':
l = []
for i in range(100):
t = Thread(target=task)
l.append(t)
t.start()
for obj in l:
obj.join()
print(n)
マルチスレッドパフォーマンステスト
純粋なコンピューティング動作は、マルチコアの利点を使用して運用効率を向上させ、複数のプロセスを使用できます。
純粋なio動作は、io動作のマルチコアの利点が反映されないため、1つのプロセスで複数のスレッドを開くことができます。
複数開くプロセス:新しいメモリスペースを開き、親プロセスのプログラムを子プロセスにコピーして、プロセスを開始します。ioの動作が発生すると、オペレーティングシステムは自動的にCPUを転送し、ioが終了した後、オペレーティングシステムはカップを再度転送します。
複数のスレッドを開き、CPUスイッチを前後に切り替えます。これは、スレッドを開くコストがプロセスを開くコストよりもはるかに低いため、効率が複数のプロセスよりも高くなるためです。
代码演示
from multiprocessing import Process
from threading import Thread
import os, time
def work():
# res = 0
# for i in range(100000000):
# res *= i
time.sleep(5)
if __name__ == '__main__':
l = []
# print(os.cpu_count()) # 本机为8核
start = time.time()
for i in range(8):
p = Process(target=work) # 纯计算 多进程花费的时间12.8s |纯io行为多进程运行时间为5.335s
# p = Thread(target=work) # 纯计算 多线程运行的时间46.45s |纯io行为多线程运行时间为5.003s
l.append(p)
p.start()
for p in l:
p.join()
stop = time.time()
print("run time is %s" % (stop - start))