【前言】
shell中并沒(méi)有真正的多線程,我們這里講的多線程,實(shí)際上是多進(jìn)程,即最大化地使用cpu。
【正文】
1. 初級(jí)版
在linux或shell中,&一般放在某條命令的末尾,可以把該條命令放到一個(gè)后臺(tái)進(jìn)程去處理。
比如:
ls &
這個(gè)時(shí)候,我們應(yīng)該可以想到一種多線程的實(shí)現(xiàn)方式。
那就是,循環(huán)。
for item in xxx
do
耗時(shí)操作 &
done
wait
這里,wait的作用就是等到所有的后臺(tái)進(jìn)程執(zhí)行完畢,才會(huì)執(zhí)行后面的命令。
這時(shí)候,出現(xiàn)一個(gè)新的問(wèn)題。
我們每循環(huán)一次就會(huì)打開一個(gè)后臺(tái)進(jìn)程,循環(huán)次數(shù)少的時(shí)候尚可,循環(huán)次數(shù)多了怎么辦?
后臺(tái)進(jìn)程開的太多,我們的系統(tǒng)難免會(huì)崩潰,那么我們?nèi)绾蜗拗坪笈_(tái)進(jìn)程開啟的數(shù)量呢?
2. 進(jìn)階版
對(duì)于上面的問(wèn)題,我們很容易想到一種解決方法。
那就是,兩層循環(huán)。
threads=20
for item in xxx
do
for((i=0;i<$threads;i++))
do
耗時(shí)操作 &
done
wait
done
這個(gè)方法,其實(shí)也是有點(diǎn)問(wèn)題的。
如果,我們同時(shí)開了20個(gè)后臺(tái)進(jìn)程,其中有一個(gè)跑的特別慢,那么即使其他19個(gè)進(jìn)程的任務(wù)都跑完了,程序還是會(huì)繼續(xù)等待。這樣下來(lái),我們的速度其實(shí)并沒(méi)有多快,這種多線程是有殘缺的多線程,并不完美。
3. 高級(jí)版(主流)
其實(shí)當(dāng)前的主流就是使用命名管道(fifo)的方式,去實(shí)現(xiàn)進(jìn)程數(shù)量的可控。
threads=50
mkfifo testfifo #新建一個(gè)fifo類型的文件
exec 100<>testfifo #以讀寫方式打開文件,并把文件描述符fd100指向該文件
rm -rf testfifo #該文件可以刪掉(原因是存在未關(guān)閉的fd,所以實(shí)際上文件并沒(méi)有真正刪除)
for((i=1;i<=${threads};i++))
do
echo >&100 #echo默認(rèn)輸出一個(gè)空行,這里循環(huán)執(zhí)行,相當(dāng)于給該文件輸入了50個(gè)空行
done
for item in xxx
do
read -u100 #從該文件中讀一行,如果沒(méi)有就會(huì)卡在這里。相當(dāng)于開啟一個(gè)線程。
{
耗時(shí)操作
echo >&100 #執(zhí)行完任務(wù)后,輸入一行到該文件。相當(dāng)于釋放一個(gè)線程。
}&
done
wait
exec 100>&- #關(guān)閉fd100
從上面的代碼和注釋應(yīng)該可以看的很清楚,其實(shí)控制進(jìn)程數(shù)量的方法就是控制向fifo文件中讀寫行。
至于為什么使用fifo文件,而不是使用普通文件。我查了一些文章以后發(fā)現(xiàn),在默認(rèn)情況下,fifo文件是阻塞的。
也就是說(shuō),如果fifo文件中沒(méi)有數(shù)據(jù),read是會(huì)卡住的,所以必須要使用fifo。實(shí)際上,fifo文件是進(jìn)程間通信的一種重要手段,有興趣的同學(xué)一定要深入看一看。
4. 番外篇(原子操作)
為什么會(huì)有這個(gè)番外篇?實(shí)際上是在工作中遇到一個(gè)需求,就是在耗時(shí)操作之后,我要打印進(jìn)度。但是打印進(jìn)度這個(gè)事情,無(wú)論如何也不可能是原子操作,那就會(huì)出現(xiàn)線程安全問(wèn)題。所以,我必須找到一種方法,可以實(shí)現(xiàn)一整塊代碼的原子操作。
那就是,加鎖。
# 以下是臨界區(qū)
(
flock -x 7 7>&7 #flock文件鎖,-x表示獨(dú)享鎖
打印進(jìn)度
)7<>temp.lock
【后記】
感謝大佬們的觀看,如果有不懂的地方可以隨時(shí)騷擾我。
如果發(fā)現(xiàn)錯(cuò)誤或可以改進(jìn)的地方,也希望大佬們不吝賜教,多謝。