前言
說起Python的多線程,很多人都嗤之以鼻,說Python的多線程是假的多線程,沒有用,或者說不好用,那本次就和大家一起來分享一下Python的多線程,看看是不是這樣的。
相關概念
線程(Thread)也叫輕量級進程,是操作系統能夠進行運算調度的最小單位,它被包含在進程之中,是進程中的實際運作單位。線程自己不擁有系統資源,只擁有一點在運行中必不可少的資源,但它可與同屬的一個進程的其它線程共享進程所擁有的全部資源。一個線程可以創建和撤銷另一個線程,同一進程中的多個線程之間可以併發執行。
多線程語法
在Python中實現多線程編程需要用到的就是threading模塊中的Thread類,我們來看看最簡單的語法,我們首先來一個簡單的函數。
def task(num):
count = 0
for i in range(num):
count += 1
print(count)
nums = [100, 1000, 10000]
for num in nums:
task(num)
# 100
#1000
#10000
我們用三個子線程分別計算。
import threading
def task(num):
count = 0
for i in range(num):
count += 1
print(count)
nums = [100, 1000, 10000]
for num in nums:
t = threading.Thread(target=task, args=(num,))
t.start()
利用Thread創建線程,target參數接收函數名,args參數接收函數的參數,start方法啓動線程。
這裏還需要講解一下join方法,他的作用是讓主線程等待,直到該子線程結束。我們來看看加該方法和不加該方法,最終的結果是怎麼樣的。
import threading
def task():
num = 0
for i in range(10000000):
num += 1
print(num)
t = threading.Thread(target=task)
t.start()
print('end')
# end
# 10000000
import threading
def task():
num = 0
for i in range(10000000):
num += 1
print(num)
t = threading.Thread(target=task)
t.start()
t.join()
print('end')
# 10000000
# end
GIL
在說概念之前,我們還是以上面的代碼爲例,分別求單線程和多線程代碼運行的時間。
單線程
import time
def task(num):
count = 0
for i in range(num):
count += 1
print(count)
nums = [1000000, 100000000, 1000000000]
start = time.time()
for num in nums:
task(num)
end = time.time()
print(end - start)
# 50.44705629348755
多線程
import threading
import time
def task(num):
count = 0
for i in range(num):
count += 1
print(count)
nums = [1000000, 100000000, 1000000000]
ts = []
start = time.time()
for num in nums:
t = threading.Thread(target=task, args=(num,))
t.start()
ts.append(t)
for t in ts:
t.join()
end = time.time()
print(end - start)
# 55.022353172302246
你會發現多線程比單線程花費的時間還要更多,這是因爲GIL的原因。
GIL的全稱是Global Interpreter Lock(全局解釋器鎖),Python最初的設計理念在於,爲了解決多線程之間數據完整性和狀態同步的問題,設計爲在任意時刻只能由一個線程在解釋器中運行。因此Python中的多線程是表面上的多線程(同一時刻只有一個線程),不是真正的多線程。
但是如果是因爲GIL的原因,就說多線程無用是不對的,對於IO密集的程序,多線程是要比單線程快的。我們舉一個簡單的爬蟲案例。
單線程
import time
def task(url):
s = url.split('_')[-1]
time.sleep(int(s)) #這裏模擬請求等待
urls = ['url_1', 'url_2', 'url_3']
start = time.time()
for url in urls:
task(url)
end = time.time()
print(end - start)
# 6.013520002365112
多線程
import threading
import time
def task(url):
s = url.split('_')[-1]
time.sleep(int(s))
ts = []
urls = ['url_1', 'url_2', 'url_3']
start = time.time()
for url in urls:
t = threading.Thread(target=task, args=(url,))
t.start()
ts.append(t)
for t in ts:
t.join()
end = time.time()
print(end - start)
# 3.005527973175049
這時候我們就能看到多線程的優勢了,雖然多線程只是在各線程來回切換,但是可以讓IO堵塞的時間切換到其他線程做其他的任務,很適合爬蟲或者文件的操作。
今天的分享就到這了,如果我的文章對你有幫助,別忘了點贊,收藏,轉發,這對我有很大的幫助,我們下期再見~