沒錯就是解毒
首先,我們來解讀一些基本的概念。在多線程開發中我們經常會遇到很多的名詞,一時間也沒辦法分清他們到底是什麼意思。所以我在這裡做個簡單的整理
並發: 統一執行很多任務,表面上同時發生,但是實際上是計算機分時處理的結果,即CPU在不同的任務之間進行切換,執行一段時間後切換到另一個線程。
並行:真正意義上的同時執行任務。兩者的區別實際上用一張圖就能很好的說明
同步:等操作完成才返回
異步:直接返回
串行隊列:一次只執行一個任務
並發隊列: 一次執行多個任務
這裡我們需要說明的是,無論是並發隊列還是並行隊列,他們都是FIFO的,也就是說先添加到隊列中的任務會先被執行,但是對於並發隊列而言,我們不能保證執行完成時的順序。另外,在一些博文中會將Concurrent Queue翻譯為 並行隊列,個人認為並不是很嚴謹。首先是因為Concurrent 本意就是並發,並且如果使用並發這個名詞,對於一些初學者來講可能會產生誤導,隊列中的任務到底是並發執行還是並行執行的呢?
進程:是操作系統對於正在運行的程序的一種抽象,在系統上可以同時運行多個進程,而每個進程都好像在獨立使用硬件
線程:一個進程實際上可以由多個線程的執行單元組成,每個線程都在進程的上下文中,共享同樣的代碼和全局數據
進程、線程和應用程序的關系:進程為應用程序開辟內存空間,而線程執行應用程序代碼
進程和線程的關系:進程由線程組成、一個進程理論上可以有很多的線程,但至少有一個主線程。
死鎖: 兩個或多個線程相互等待而導致任何一個線程都不能執行。
我們經常會看到以下代碼
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ //[self do something]; });
這段代碼表示我們“開啟一個異步線程,在全局隊列中執行”
那麼我們如果我們將dispatch_get_global_queue()
函數改為dispatch_get_main_queue()
就會說我們在主線程上做處理事務,而事實上我們使用這個函數創建或得到的是一個隊列而非線程。
更准確的說,我認為,我們使用GCD的時候應該是面向隊列,而不是線程。在很多的博客中都會使用NSLog(@"current thread is %@", [NSThread currentThread])
這段代碼來打印當前的線程,然後告訴你說我們在XX線程上執行了代碼。的確,這樣的方式更加直接的說明了問題。但是就像我之前提到的,開發者應該面向的是隊列而不是線程,所以這只是輔助說明的一種方式。
OK,接下來我們討論下dispatch_async和dispatch_sync函數都干了些什麼。開始我以為他們是創建線程用的,就如一些的博文中會講到,“每執行一次任務的時候,dispatch_async總會為我們開辟一個新的線程”
我在文檔裡看到如下說明
dispatch_sync
Submits a block object for execution on a dispatch queue and waits until that block completes.
Submits a block to a dispatch queue for synchronous execution. Unlike dispatch_async, this function does not return until the block has finished. Calling this function and targeting the current queue results in deadlock
dispatch_async
Submits a block for asynchronous execution on a dispatch queue and returns immediately.
This function is the fundamental mechanism for submitting blocks to a dispatch queue. Calls to this function always return immediately after the block has been submitted and never wait for the block to be invoked. The target queue determines whether the block is invoked serially or concurrently with respect to other blocks submitted to that same queue. Independent serial queues are processed concurrently with respect to each other.
這裡我認為,這兩個函數只是用於提交任務或者說block的。他們唯一的區別是會不會等待任務完成。對於dispatch_sync
而言,他會提交任務,同時阻塞當前的線程直到任務執行完成才會返回。 dispatch_async
則會在提交任務之後直接返回,不阻塞和等待。如此一來,真正和與線程相關的就可能是隊列了。
前面提到的在GCD中我們只是面向隊列和任務。隊列包括串行隊列和並發隊列,而任務提交是同步或者異步的。那麼我們就來看下這些概念排列組合後的場景。
說明:在調用dispatch_sync方法的時候我們指定了添加任務的隊列和任務(block)。那麼該方法就會提交該任務到串行隊列中,然後然後阻塞線程,等待任務完成之後再返回。對於串行隊列,前面的概念解釋裡已經說過了,任務一個接一個執行。
說明:異步提交的時候,dispatch_async函數提交然後直接返回。區別是並發隊列會一次執行多個任務。
我們在從代碼部分去理解同步和異步的問題
左側兩段輸出,我們可以看到,都是同步提交,無論是提交到串行隊列還是並發隊列,執行的順序都是一樣的。並且提交任務前後的代碼的順序都是依次執行的。
在看右側部分,由於異步提交後直接返回不等待執行完成。所以任務提交前後的代碼會先於任務中的代碼執行。
接下來,我們在從代碼的角度去理解串行隊列和並發隊列
在同樣是異步的前提下,任務前後的代碼都先於任務執行了(當然這並不是絕對的)。我們看到在串行隊列中,執行任務的線程不在主線程中,但是他們都是在一個線程中去處理。也正因為任務前後的代碼執行和任務代碼執行的線程不同,所以我們並不能保證任務部分的代碼和任務後的代碼的執行先後性。因此會出現任務代碼先執行,在執行過程又出現任務後的打印輸出。
同樣是同步提交的情況下,兩個輸出完全一致。串行隊列同步提交的情況我們可以理解,因為它一次只執行一個任務,所以線程號是一樣的。但是,並發隊列的情況下,該怎麼理解?其實也很簡單,因為他是同步提交,需要等到當前任務完成後返回再進行後續的操作,所以即使是並發的隊列,我們看到的效果還是和串行一樣。
首先很感謝你能看到這裡,聽我瞎扯那麼多。
以上是我個人的理解,感覺與一些博文中的思想有些出入,所有我也不是很確定。我僅以
我看到的為參考,並提出自己的理解。
目前還有以下問題暫時沒有想通:
從文檔中有看到系統實際上有維持一個線程池,但是沒有說清隊列和它有什麼具體的關系。
如下圖,同樣是在主線程中執行,但是如果將這個queue改成main queue時就會報錯。
歸根結底還是沒有理清線程和隊列的關系,或者說是主線,隊列,其它線程這三者的關系。
如果覺得我的想法有問題的同學,請幫忙指正,萬分感謝。
如果覺得我理解有道理的同學,希望能一起探討一下遺留的問題,共同完善知識體系:]
最後放上源代碼
GCD解毒Demo