Python Web学习笔记之Python多线程基础
多線程理解
多線程是多個(gè)任務(wù)同時(shí)運(yùn)行的一種方式。比如一個(gè)循環(huán)中,每個(gè)循環(huán)看做一個(gè)任務(wù),我們希望第一次循環(huán)運(yùn)行還沒結(jié)束時(shí),就可以開始第二次循環(huán),用這種方式來節(jié)省時(shí)間。
python中這種同時(shí)運(yùn)行的目的是最大化利用CPU的計(jì)算能力,將很多等待時(shí)間利用起來。這也說明如果程序耗時(shí)不是因?yàn)榈却龝r(shí)間,而是任務(wù)非常多,就是要計(jì)算那么久,則多線程無法改善運(yùn)行時(shí)間。
更多有關(guān)多線程理解的內(nèi)容可以參考下面資料
- 廖雪峰教程
- 知乎回答
- 百度一下還有非常多的說明,這里不再贅述
?
簡(jiǎn)單使用
先看下面這個(gè)函數(shù)
import time def myfun():time.sleep(1)a = 1 + 1print(a)
如果我們要運(yùn)行10次這個(gè)函數(shù),它的運(yùn)行時(shí)間主要在于每次sleep的那一秒,1 + 1的計(jì)算是不會(huì)耗多少時(shí)間的。這種情況可以用多線程提高效率。
下面來看一下不使用多線程耗時(shí)和使用多線程的耗時(shí)
不使用多線程
t = time.time() for _ in range(5):myfun() print(time.time() - t)
得到結(jié)果是 5.002434492111206
下面我們使用多線程
from threading import Thread for _ in range(5):th = Thread(target = myfun)th.start()
這樣使用多線程其實(shí)就可以了,你會(huì)發(fā)現(xiàn)大概1秒,5個(gè)2會(huì)同時(shí)出來,說明5次循環(huán)其實(shí)幾乎同時(shí)運(yùn)行,5次1秒的等待時(shí)間同時(shí)進(jìn)行,最后只等待了1秒。
這里多線程只包括了兩步
- 用
Thread增加一個(gè)線程,這里是將每一次循環(huán)作為一次新的線程,一個(gè)線程執(zhí)行一次myfun函數(shù)。 - 用
start()開始運(yùn)行這個(gè)線程,每個(gè)線程都需要這樣顯式開啟才會(huì)運(yùn)行。一個(gè)線程這樣開啟后,不需要等待它運(yùn)行完成,就可以繼續(xù)運(yùn)行下面的程序,即下一次循環(huán)(然后又新建了第二個(gè)線程,運(yùn)行未結(jié)束即開啟第三個(gè)……)
這里要注意一點(diǎn):多線程是放在循環(huán)里面的,不能定義好循環(huán)之后,從外面將它變成多線程。
讀者可能會(huì)注意到,不用多線程時(shí)是通過程序計(jì)算時(shí)間的,使用多線程卻沒有。這是因?yàn)橐?jì)算時(shí)間需要增加一些代碼,無法展示最簡(jiǎn)單的多線程使用,所以就先不計(jì)算時(shí)間。接下來我們就講join()的使用,并計(jì)算時(shí)間。
join的使用
線程的join()方法表示等這個(gè)線程運(yùn)行完畢,程序再往下運(yùn)行。我們來看下面的例子
from threading import Thread t = time.time() for _ in range(5):th = Thread(target = myfun)th.start()th.join() print(time.time() - t) # 結(jié)果為 5.0047078132629395 秒
這里start()之后馬上join(),表示每一個(gè)線程都要運(yùn)行結(jié)束才能進(jìn)行下一次循環(huán),這樣就和沒有使用多線程沒有區(qū)別了。不過如果要計(jì)算多線程運(yùn)行時(shí)間卻是要用到這個(gè)join()
我們先看一下不用join()的情況
from threading import Thread t = time.time() for _ in range(5):th = Thread(target = myfun)th.start() print(time.time() - t) # 結(jié)果為 0.0009980201721191406 秒
它連1秒都沒有等,就輸出了結(jié)果,而且5個(gè)2是在打印出這個(gè)之后才輸出出來的。這是因?yàn)?code>print(time.time() - t)是區(qū)別于那5次循環(huán)線程之外的第6個(gè)線程,它不會(huì)等待5個(gè)線程運(yùn)行結(jié)束就會(huì)開始運(yùn)行。所以這樣是無法獲得上面5個(gè)線程的運(yùn)行時(shí)間的,我們需要用join()等待5個(gè)線程都運(yùn)行結(jié)束。
代碼如下
from threading import Thread t = time.time() ths = [] for _ in range(5):th = Thread(target = myfun)th.start()ths.append(th) for th in ths:th.join() print(time.time() - t) # 結(jié)果為 1.0038363933563232
上面定義ths列表存儲(chǔ)這些線程,最后用循環(huán)確保每一個(gè)線程都已經(jīng)運(yùn)行完成再計(jì)算時(shí)間差。
join()不只是用于這種情形。當(dāng)一步代碼運(yùn)行依賴之前代碼運(yùn)行完成時(shí),就要加入join()命令。
現(xiàn)在我們已經(jīng)學(xué)完了多線程的一般使用方法,可以在多數(shù)場(chǎng)景使用了。下面來介紹一些細(xì)節(jié)
其他
(1)線程名稱
我們直接看下面的代碼
import threading print(threading.current_thread().getName()) def myfun():time.sleep(1)print(threading.current_thread().name)a = 1 + 1 for i in range(5):th = threading.Thread(target = myfun, name = 'thread {}'.format(i))th.start() # 輸出結(jié)果 MainThread thread 0 thread 1 thread 4 thread 3 thread 2
解釋一下
threading.current_thread()表示當(dāng)前線程,可以調(diào)用name或getName()獲取線程名稱- 任何進(jìn)程都會(huì)默認(rèn)啟動(dòng)一個(gè)線程,默認(rèn)名稱為
MainThread,也就是主程序占一個(gè)線程,這個(gè)線程和之后用Thread新加的線程是相互獨(dú)立的,主線程不會(huì)等待其余線程運(yùn)行結(jié)束就會(huì)繼續(xù)往下運(yùn)行。之前不用join()無法計(jì)算運(yùn)行時(shí)間就是因?yàn)橹骶€程先運(yùn)行完了。 Thread表示運(yùn)行這個(gè)函數(shù)啟動(dòng)一個(gè)新的線程,在其中加一個(gè)name參數(shù)指定這個(gè)函數(shù)線程名,則在這個(gè)函數(shù)內(nèi)打印線程名就顯示這里name參數(shù)對(duì)應(yīng)值- 在循環(huán)中打印有兩種。第一種
print(threading.current_thread().name)則是MainThread;第二種print(th.name)則是thread 1等
(2)Thread函數(shù)
上面我們使用了Thread函數(shù)的target name參數(shù),下面來說一下它的其他參數(shù)
args指定target對(duì)應(yīng)函數(shù)的參數(shù),用元組傳入,比如args = (3, )daemon主線程默認(rèn)是False,如果沒有指定則繼承父線程的值。True則如果主線程運(yùn)行結(jié)束,該線程也停止運(yùn)行;False則該線程會(huì)繼續(xù)運(yùn)行直到運(yùn)行結(jié)束,無視主線程如何。(要看這個(gè)參數(shù)的效果要在py文件中編寫代碼,在cmd里運(yùn)行,不能在jupyter notebook里,因?yàn)檫@里會(huì)多出一些線程干擾)group是預(yù)留的一個(gè)參數(shù),用于以后擴(kuò)展ThreadGroup類,現(xiàn)在沒用
(3)Thread對(duì)象
上面threading.Thread和threading.current_thread()都創(chuàng)建了一個(gè)Thread對(duì)象,Thread對(duì)象有如下屬性和方法
getName() .name獲取線程名setName()設(shè)置線程名start() join()這兩個(gè)之前說過了join()有一個(gè)timeout參數(shù),表示等待這個(gè)線程結(jié)束時(shí),如果等待時(shí)間超過這個(gè)時(shí)間,就不再等,繼續(xù)進(jìn)行下面的代碼,但是這個(gè)線程不會(huì)被中斷run()也是運(yùn)行這個(gè)線程,但是必須等到這個(gè)線程運(yùn)行結(jié)束才會(huì)繼續(xù)執(zhí)行之后的代碼(如果將上面的start全換成run則相當(dāng)于沒有開多線程)is_alive()如果該線程還沒運(yùn)行完,就是True否則Falsedaemon返回該線程的daemonsetDaemon(True)設(shè)置線程的daemon
(4)threading
一些直接調(diào)用的變量
threading.currentThread(): 返回當(dāng)前的線程變量threading.enumerate(): 返回一個(gè)包含正在運(yùn)行的線程的listthreading.activeCount(): 返回正在運(yùn)行的線程數(shù)量,與len(threading.enumerate())有相同的結(jié)果
?
?
參考
轉(zhuǎn)載于:https://www.cnblogs.com/JetpropelledSnake/p/8931194.html
總結(jié)
以上是生活随笔為你收集整理的Python Web学习笔记之Python多线程基础的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 空气悬挂多少钱啊?
- 下一篇: C# window服务操作