線程和同步異步的知識,在我們剛踏入程序這一行業(yè)的時(shí)候就開始接觸了。
但是由于I/O(磁盤讀寫,網(wǎng)絡(luò)通信)這種耗時(shí)的操作,各個語言都有完整的封裝方法,我們并不用去了解其異步過程也能完成大部分功能。所以多數(shù)人在工作一兩年之后,仍然無法理清他們的關(guān)系。
不過它對于我們優(yōu)化代碼,了解底層不可或缺,現(xiàn)在我來盤點(diǎn)一下他們的聯(lián)系和概念。
一.核與線程的關(guān)系
我們平時(shí)經(jīng)常會聽到8核、16核處理器,那這里的核與線程有什么關(guān)系呢?單核代表一個線程么?
單核可以有多個線程,只是它的多線程通過分時(shí)來實(shí)現(xiàn),即把時(shí)間分成片,每片處理一個線程,所有的線程循環(huán)處理。所以對于單核系統(tǒng)而言,開多少個線程都無法提高程序的運(yùn)行效率。
而多核可以同時(shí)實(shí)現(xiàn)多線程。比如雙核系統(tǒng)開兩個線程,運(yùn)行效率將會翻倍。但并不代表效率可以無限提升,多線程個數(shù)等于核的個數(shù)時(shí),效率達(dá)到最高。
比較另類的是,之前Intel有過16核32線程的機(jī)器,它的原理是超線程技術(shù)使一個核心能模擬2個邏輯核心,但據(jù)說并不能真正達(dá)到兩個核的水平。
打個形象的比喻:(每種動作代表一個線程)
單核單線程:一個人喝完酒,然后抽煙,最后吃燒鴨
單核多線程:一個人喝一口酒,抽一口煙,吃一口烤鴨,同時(shí)進(jìn)行
多喝多線程:三個人,一個人喝酒,一個人抽煙,一個人吃烤鴨,互不干擾
二.同步異步、阻塞非阻塞
首先,我們先來談一下同步異步的概念,我查過很多資料,眾說紛紜,每個版本的講法都不一樣,出入很大。但其實(shí)了解這些概念是為了讓我們更加明確整個底層運(yùn)作方式,所以我挑了一個比較鮮明的概念供大家參考。
(我們iOS平時(shí)說到的同步大意和這里的同步阻塞對等,異步與異步非阻塞對等,但是由于js中存在比較重要的同步非阻塞,所以我在這里拆開來講)
1、同步(sync):
發(fā)出一個功能調(diào)用時(shí),在沒有得到結(jié)果之前,該調(diào)用就不返回。
2、異步(async):
與同步相對,調(diào)用在發(fā)出之后,這個調(diào)用就直接返回了,所以沒有返回結(jié)果。當(dāng)這個調(diào)用完成后,一般通過狀態(tài)、通知和回調(diào)來通知調(diào)用者。對于異步調(diào)用,調(diào)用的返回并不受調(diào)用者控制。
對于通知調(diào)用者的三種方式,具體如下:
狀態(tài):即監(jiān)聽被調(diào)用者的狀態(tài)(輪詢),調(diào)用者需要每隔一定時(shí)間檢查一次,效率會很低。
通知:當(dāng)被調(diào)用者執(zhí)行完成后,發(fā)出通知告知調(diào)用者,無需消耗太多性能。
回調(diào):與通知類似,當(dāng)被調(diào)用者執(zhí)行完成后,會調(diào)用調(diào)用者提供的回調(diào)函數(shù)。
3、阻塞(block):
阻塞調(diào)用是指調(diào)用結(jié)果返回(或者收到通知)之前,當(dāng)前線程會被掛起,即不繼續(xù)執(zhí)行后續(xù)操作。
簡單來說,等前一件做完了才能做下一件事。
4、非阻塞(non-block):
非阻塞調(diào)用指在不能立刻得到結(jié)果之前,該調(diào)用不會阻塞當(dāng)前線程。
總結(jié):所以所謂同步異步,是對于被調(diào)用者而言的;而阻塞非阻塞,則是對調(diào)用者而言的。
和剛才一樣,打個比喻加深理解:
1、同步阻塞:你打電話給114查路線,在不掛斷的情況下,客服幫你查了十分鐘之后告訴你,期間你一直在接聽電話。
2、同步非阻塞:你打電話給114查路線,在不掛斷的情況下,客服幫你查了十分鐘之后告訴你,你期間吃了個??(打電話沒有影響你做其他事,顯示運(yùn)用中也很難遇到)。
3、異步非阻塞:你打電話給114查路線,客服說查好之后打給你,這期間你可以做任何事。
4、異步阻塞:你打電話給114查路線,客服說查好之后打給你,但這期間你什么都沒做,等到回復(fù)電話之后,再繼續(xù)下一步動作。(是不是很傻)現(xiàn)實(shí)運(yùn)用中,異步阻塞是沒有意義的!
三.線程與同步異步的關(guān)系
單線程可以異步操作么?當(dāng)然可以。比如延遲方法,就是典型的異步。
但是單線程異步和多線程異步還是有區(qū)別的,因?yàn)閱尉€程異步操作在通知調(diào)用者之前,是沒有跑任何的代碼的,因?yàn)闆]有任何的線程提供給它。
其實(shí)由于各方概念的差異,也可以說單線程不能進(jìn)行異步操作,所謂延遲方法,不過是過一段時(shí)間之后,把執(zhí)行函數(shù)加入主隊(duì)列之中,兩種說法都存在。
老規(guī)矩,打比方(這里的同步異步代表同步阻塞和異步非阻塞)
1、單線程同步:甲先搬了一塊磚,回頭又搬了另一塊磚
2、多線程同步:甲先搬了一塊磚,乙又搬了一塊磚(要用到線程間的同步機(jī)制)
3、多線程異步:甲乙同時(shí)各搬了一塊磚
四.js的坑,既然名義上是單線程的,卻為何能進(jìn)行真正意義上異步操作
nodeJS編程模式:http://www.cnblogs.com/wwicked/articles/4770416.html??
知乎詳細(xì)鏈接:https://www.zhihu.com/question/20866267?
JS的單線程是指一個瀏覽器進(jìn)程中只有一個JS的執(zhí)行線程,同一時(shí)刻內(nèi)只會有一段代碼在執(zhí)行(你可以使用IE的標(biāo)簽式瀏覽試試看效果,這時(shí)打開的多個頁面使用的都是同一個JS執(zhí)行線程,如果其中一個頁面在執(zhí)行一個運(yùn)算量較大的function時(shí),其他窗口的JS就會停止工作)。
而異步機(jī)制是瀏覽器的兩個或以上常駐線程共同完成的,例如異步請求是由兩個常駐線程:JS執(zhí)行線程和事件觸發(fā)線程共同完成的,JS的執(zhí)行線程發(fā)起異步請求(這時(shí)瀏覽器會開一條新的HTTP請求線程來執(zhí)行請求,這時(shí)JS的任務(wù)已完成,繼續(xù)執(zhí)行線程隊(duì)列中剩下的其他任務(wù)),然后在未來的某一時(shí)刻事件觸發(fā)線程監(jiān)視到之前的發(fā)起的HTTP請求已完成,它就會把完成事件插入到JS執(zhí)行隊(duì)列的尾部等待JS處理。又例如定時(shí)觸發(fā)(settimeout和setinterval)是由瀏覽器的定時(shí)器線程執(zhí)行的定時(shí)計(jì)數(shù),然后在定時(shí)時(shí)間把定時(shí)處理函數(shù)的執(zhí)行請求插入到JS執(zhí)行隊(duì)列的尾端(所以用這兩個函數(shù)的時(shí)候,實(shí)際的執(zhí)行時(shí)間是大于或等于指定時(shí)間的,不保證能準(zhǔn)確定時(shí)的)。
所以,所謂的JS的單線程和異步更多的應(yīng)該是屬于瀏覽器的行為,他們之間沒有沖突,更不是同一種事物,沒有什么區(qū)別不區(qū)別的。
五.ios的多線程問題
做iOS 的應(yīng)該都有一個體會,總覺得block是異步的,其實(shí)這是錯誤的理解。
block的線程和它的實(shí)現(xiàn)方法處的線程一致,所以我們在調(diào)用時(shí),可以檢查一下它的實(shí)現(xiàn)代碼。
我們平時(shí)經(jīng)常討論的同步問題多發(fā)生在多線程環(huán)境中的數(shù)據(jù)共享問題,當(dāng)多個線程同時(shí)操作一個對象時(shí),會出現(xiàn)意想不到的bug,所以這時(shí)應(yīng)該加上線程鎖。
這篇文章總結(jié)了多篇博客,但卻沒有完全照抄,加入了很多自己的理解,希望大家多多支持。