多線程
目前市面上常見(jiàn)的系統(tǒng),如windows、mac os、Linux都支持多線程
什么是多任務(wù)
操作系統(tǒng)同時(shí)執(zhí)行多個(gè)任務(wù)
單核CPU如何實(shí)現(xiàn)多任務(wù)吉嚣?
表面看,每個(gè)任務(wù)都是同時(shí)執(zhí)行蹬铺,實(shí)際上每個(gè)任務(wù)都在輪詢著執(zhí)行尝哆;
只是因?yàn)镃PU調(diào)度和執(zhí)行速度太快,導(dǎo)致我們感覺(jué)是所有任務(wù)都在同時(shí)執(zhí)行
多核CPU如何實(shí)現(xiàn)多任務(wù)甜攀?
當(dāng)任務(wù)數(shù)小于CPU核心數(shù)時(shí)秋泄,是真正的多任務(wù)
當(dāng)任務(wù)數(shù)大于CPU核心數(shù)時(shí),多余的任務(wù)會(huì)再次被分配赴邻,分配到的CPU進(jìn)行輪詢執(zhí)行
并發(fā)與并行
并發(fā):表面看時(shí)一起執(zhí)行印衔,但是任務(wù)數(shù)多余CPU核心數(shù)
并行:一起執(zhí)行啡捶,但是任務(wù)數(shù)小于CPU數(shù)
實(shí)現(xiàn)多任務(wù)的方式
1.多進(jìn)程方式 多操作系統(tǒng)而言姥敛,一個(gè)任務(wù)就是一個(gè)進(jìn)程
2.多線程方式
3.協(xié)程方式
4.多進(jìn)程+多線程方式
python中的多進(jìn)程
multiprocessing 多進(jìn)程
from multiprocessing import Process
import time
def func():
while True:
print("這也是一個(gè)python進(jìn)程")
time.sleep(1.5)
if __name__ == '__main__':
print("父進(jìn)程啟動(dòng)...")
p = Process(target=func)
p.start()
while True:
print("這是一個(gè)python進(jìn)程")
time.sleep(1)
帶有參數(shù)的多進(jìn)程
from multiprocessing import Process
import time
def func(str):
while True:
print("這也是一個(gè)%s進(jìn)程"%str)
time.sleep(1.5)
if __name__ == '__main__':
print("父進(jìn)程啟動(dòng)...")
p = Process(target=func, args=("python",))
p.start()
while True:
print("這是一個(gè)python進(jìn)程")
time.sleep(1)
多進(jìn)程中的變量
.join() 表示父進(jìn)程等待子進(jìn)程結(jié)束再繼續(xù)執(zhí)行
每個(gè)進(jìn)程都有自己的一套代碼段、數(shù)據(jù)段和堆棧段瞎暑,互不干擾彤敛;
也就是說(shuō)父進(jìn)程中的全局變量會(huì)在子進(jìn)程創(chuàng)建時(shí)備份一份与帆,
如果在子進(jìn)程中進(jìn)行更改不會(huì)影響父進(jìn)程中的變量;
同樣如果父進(jìn)程在創(chuàng)建了子進(jìn)程后再對(duì)全局變量進(jìn)行修改墨榄,同樣不會(huì)對(duì)子進(jìn)程的變量造成影響
Pocess中傳遞參數(shù)必須為可迭代對(duì)象
from multiprocessing import Process
import time
num = 100
def func(num):
print("子進(jìn)程啟動(dòng)...")
time.sleep(1)
num += 1
print(num)
print("子進(jìn)程結(jié)束...")
if __name__ == '__main__':
print("父進(jìn)程啟動(dòng)...")
p = Process(target=func, args=(num,))
# 此時(shí)子進(jìn)程已創(chuàng)建玄糟,再修改父進(jìn)程中的全局變量將不會(huì)影響子進(jìn)程
num += 2
p.start()
print("父進(jìn)程結(jié)束...%d" % num)
進(jìn)程池
1.進(jìn)程池需要導(dǎo)入 multiprocessing 中的Pool
2.創(chuàng)建進(jìn)程池時(shí)Pool()中的參數(shù)表示同時(shí)啟動(dòng)的進(jìn)程數(shù)
如果不寫(xiě)則表示本機(jī)的邏輯核心數(shù)
3.進(jìn)程池中進(jìn)程啟動(dòng)不用寫(xiě) start 語(yǔ)句
4.進(jìn)程池必須先關(guān)閉才能join
5.進(jìn)程池join后,父進(jìn)程會(huì)等待池內(nèi)所有進(jìn)程執(zhí)行完畢再向下執(zhí)行
6.進(jìn)程池內(nèi)的進(jìn)程啟動(dòng)順序由操作系統(tǒng)來(lái)決定
from multiprocessing import Pool
import time, random
def func(num):
print("子進(jìn)程%d啟動(dòng)" % num)
start = time.time()
time.sleep(random.choice([1, 2, 3]))
end = time.time()
print("子進(jìn)程%d結(jié)束袄秩,耗時(shí)%d秒" % (num, end-start))
def func1():
print("我是一個(gè)子進(jìn)程阵翎,我負(fù)責(zé)計(jì)算1+1=2")
if __name__ == '__main__':
print("父進(jìn)程啟動(dòng)")
# Pool中的參數(shù)表示同時(shí)啟動(dòng)進(jìn)程的數(shù)量,如果不寫(xiě)則表示本機(jī)邏輯核心數(shù)
# 進(jìn)程池中的進(jìn)程啟動(dòng)順序由系統(tǒng)來(lái)決定
pp = Pool(2)
# # 此處為用for循環(huán)建立多個(gè)子進(jìn)程之剧,但是子進(jìn)程的功能都相同
# for i in range(10):
# pp.apply_async(func, args=(i,))
# 此處為單個(gè)建立子進(jìn)程郭卫,子進(jìn)程的功能可以不同
pp.apply_async(func, args=(1,))
pp.apply_async(func1)
# 進(jìn)程池中進(jìn)程不需要寫(xiě)start語(yǔ)句
# 進(jìn)程池進(jìn)行join時(shí)必須先關(guān)閉
pp.close()
# 進(jìn)程池join后父進(jìn)程會(huì)等待進(jìn)程池中所有程序結(jié)束才繼續(xù)執(zhí)行
pp.join()
print("父進(jìn)程結(jié)束")
多線程混亂
多個(gè)線程對(duì)同一數(shù)據(jù)進(jìn)行更改時(shí),次數(shù)越大背稼,發(fā)生錯(cuò)誤或線程混亂的幾率越大
import threading
num = 0
def run(n):
global num
for i in range(1000000):
num = num + n
num = num - n
# print(num)
if __name__ == '__main__':
t1 = threading.Thread(target=run, args=(6,))
t2 = threading.Thread(target=run, args=(9,))
t1.start()
t2.start()
t1.join()
t2.join()
print("num = ",num)
鎖對(duì)象
為了確保一段代碼只能由一個(gè)線程從頭到尾的完整執(zhí)行
我們引入了鎖對(duì)象贰军,來(lái)限制進(jìn)行的并發(fā)執(zhí)行,避免多線程并發(fā)引起的數(shù)據(jù)混亂
但是會(huì)大大的降低效率
import threading
# 所對(duì)象
lock = threading.Lock()
num = 0
def run(n):
global num
for i in range(1000000):
"""
# 第一種寫(xiě)法
# 加鎖
lock.acquire()
num += n
num -= n
# 解鎖
lock.release()
"""
# 第二種寫(xiě)法
with lock:
num += n
num -= n
if __name__ == '__main__':
t1 = threading.Thread(target=run, args=(10,))
t2 = threading.Thread(target=run, args=(4,))
t1.start()
t2.start()
t1.join()
t2.join()
print("num =", num)
信號(hào)量:Semaphore
限定多線程并發(fā)數(shù)量
線程結(jié)束數(shù)量:Barrier
限定多線程的結(jié)束數(shù)量,如果數(shù)量不足蟹肘,則等待词疼,
直到數(shù)量滿足要求再結(jié)束
如果二者同時(shí)使用,注意并發(fā)數(shù)量應(yīng)大于結(jié)束數(shù)量
否則線程無(wú)法結(jié)束帘腹,無(wú)法繼續(xù)并發(fā)贰盗,程序會(huì)一直處于等待中
import threading,time
# 設(shè)置多線程并發(fā)數(shù)量
sem = threading.Semaphore(4)
# 設(shè)置指定數(shù)量結(jié)束
bar = threading.Barrier(3)
def run():
with sem:
print("%s--start"%threading.current_thread().name)
time.sleep(1)
bar.wait()
print("%s--end"%threading.current_thread().name)
if __name__ == '__main__':
for i in range(10):
t = threading.Thread(target=run)
t.start()