一:并發(fā)和并行
并發(fā):指的是任務數(shù)多余cpu核數(shù),通過操作系統(tǒng)的各種任務調(diào)度算法,實現(xiàn)用多個任務“一起”執(zhí)行(實際上總有一些任務不在執(zhí)行,因為切換任務的速度相當快,看上去一起執(zhí)行而已)
并行:指的是任務數(shù)小于等于cpu核數(shù),即任務真的是一起執(zhí)行

image.png

image.png
二:threading模塊
2-1:Thread類
創(chuàng)建線程對象的參數(shù):
target:指定任務函數(shù)
name:設(shè)置線程名
args:給任務函數(shù)傳參
kwargs:給任務函數(shù)傳參
daemon:設(shè)置是否作為守護線程
2-2:方法
start:啟動線程執(zhí)行
join:設(shè)置主線程等待子線程執(zhí)行
2-2-1:方法的使用
import time
from threading import Thread
def func1():
for i in range(5):
print("------正在做事情1-----")
time.sleep(1)
def func2():
for i in range(6):
print("------正在做事情2-----")
time.sleep(1)
st = time.time()
#創(chuàng)建一個線程對象
t1 = Thread(target=func1)
t2 = Thread(target = func2)
# 啟動線程執(zhí)行
t1.start()
t2.start()
# 主線程等待t1執(zhí)行完,再往下執(zhí)行
t1.join()
# 主線程等待t2執(zhí)行完,在往下執(zhí)行
t2.join()
# 需求:主線程等待子線程執(zhí)行完
et = time.time()
print(et-st)

image.png
2-3:守護線程
設(shè)置子線程守護主線程執(zhí)行
2-3-1:守護線程,及傳參
def func1(aa):
for i in range(5):
print("{}------正在做事情1-----".format(aa))
time.sleep(1)
def func2(aaa):
for i in range(6):
print("{}------正在做事情2-----".format(aaa))
time.sleep(1)
# 創(chuàng)建一個線程對象
t1 = Thread(target=func1,name="木森1",daemon=True,args=("張三",))
t2 = Thread(target=func2,name="木森2",daemon=True,kwargs={"aaa":"李四"})
# 啟動線程執(zhí)行
t1.start()
t2.start()
time.sleep(2)
print("---主線程執(zhí)行結(jié)束---")

image.png
三:線程鎖(解決多線程共享全局變量)
線程之間共用同一塊內(nèi)存,因此線程可以共享全局變量,如果多個線程同時對同一個全局變量操作,會出現(xiàn)資源競爭問題,從而數(shù)據(jù)結(jié)果會不正確。如下案例,a的預期結(jié)果應是200000,但實際結(jié)果與預期結(jié)果不符
from threading import Thread
a = 0
def work():
global a
for i in range(100000):
a += 1
print("work執(zhí)行完:a",a)
def work2():
global a
for i in range(100000):
a += 1
print("work2執(zhí)行完:a",a)
t1 = Thread(target=work)
t2 = Thread(target=work2 )
t1.start()
t2.start()
t1.join()
t2.join()
print("a:",a)

image.png
3-1:解決方案:Lock鎖
控制線程的執(zhí)行,避免同時獲取數(shù)據(jù),線程同步能夠保證多個線程安全訪問競爭資源,最簡單的同步機制是引入互斥鎖?;コ怄i為資源引入的一個狀態(tài)方法acquire():鎖定/方法release():釋放鎖,待鎖定。某個線程要更改共享數(shù)據(jù)時,先將其鎖定,此時資源的狀態(tài)為”鎖定“,其他線程不能更改直到該線程釋放資源,將資源的狀態(tài)變成”非鎖定“,其他的線程才能再次鎖定該資源?;コ怄i保證了每次只有一個線程進行寫入操作,從而保證了多線程情況下數(shù)據(jù)的正確性。
from threading import Thread,Lock
a = 0
# 實例化一個鎖對象
lockA = Lock()
def work():
global a
for i in range(100000):
lockA.acquire()
a += 1
lockA.release()
print("work執(zhí)行完:a",a)
def work2():
global a
for i in range(100000):
lockA.acquire()
a += 1
lockA.release()
print("work2執(zhí)行完:a",a)
t1 = Thread(target=work)
t2 = Thread(target=work2 )
t1.start()
t2.start()
t1.join()
t2.join()
print("a:",a)

image.png
四:練習題
一個列表中有100個url地址,每個地址請求一次,請設(shè)計程序一個程序,
使用4個線程去發(fā)送這 100個請求(假設(shè)請求每個地址需要0.5秒,請求的代碼用time.sleep(0.5)代替),
計算一共需要多長時間計算出總耗時!
import time
from threading import Thread
url_list = [f"https://www.baidu.com-{i}" for i in range(100)]
def work():
while url_list:
url = url_list.pop()
print(url)
time.sleep(0.5)
def main():
# 創(chuàng)建4個線程
start_time = time.time()
ts = []
for i in range(4):
t1 = Thread(target=work)
t1.start()
ts.append(t1)
for t in ts:
t.join()
end_time = time.time()
print('執(zhí)行時間為:', end_time - start_time)
main()