iOS中多線程原理與runloop介紹

.線程概述

有些程序是一條直線,起點(diǎn)到終點(diǎn)蕉斜;有些程序是一個圓,不斷循環(huán)缀棍,直到將它切斷宅此。直線的如簡單的Hello World,運(yùn)行打印完,它的生命周期便結(jié)束了爬范,像曇花一現(xiàn)那樣父腕;圓如操作系統(tǒng),一直運(yùn)行直到你關(guān)機(jī)青瀑。

一個運(yùn)行著的程序就是一個進(jìn)程或者叫做一個任務(wù)侣诵,一個進(jìn)程至少包含一個線程,線程就是程序的執(zhí)行流狱窘。Mac和iOS中的程序啟動杜顺,創(chuàng)建好一個進(jìn)程的同時,一個線程便開始運(yùn)行蘸炸,這個線程叫主線程躬络。主線程在程序中的地位和其他線程不同,它是其他線程最終的父線程搭儒,且所有界面的顯示操作即AppKit或UIKit的操作必須在主線程進(jìn)行穷当。

系統(tǒng)中的每一個進(jìn)程都有自己獨(dú)立的虛擬內(nèi)存空間,而同一個進(jìn)程中的多個線程則共用進(jìn)程的內(nèi)存空間淹禾。每創(chuàng)建一個新的線程馁菜,都需要一些內(nèi)存(如每個線程有自己的Stack空間)和消耗一定的CPU時間。另外當(dāng)多個線程對同一個資源出現(xiàn)爭奪的時候需要注意線程安全問題铃岔。

.創(chuàng)建線程

創(chuàng)建一個新的線程就是給進(jìn)程增加了一個執(zhí)行流汪疮,執(zhí)行流總得有要執(zhí)行的代碼吧,所以新建一個線程需要提供一個函數(shù)或者方法作為線程的入口。

1.使用NSThread

NSThread提供了創(chuàng)建線程的途徑智嚷,還可以提供了檢測當(dāng)前線程是否是主線程的方法卖丸。使用NSThread創(chuàng)建一個新的線程有兩種方式:

1.創(chuàng)建一個NSThread的對象,調(diào)用其start方法盏道。對于這種方式的NSThread對象的創(chuàng)建稍浆,可以使用一個目標(biāo)對象的方法初始化一個NSThread對象,或者創(chuàng)建一個繼承NSThread類的子類猜嘱,實現(xiàn)其main方法衅枫,然后在直接創(chuàng)建這個子類的對象。

2.使用detachNewThreadSelector:toTarget:withObject:這個類方法創(chuàng)建一個線程朗伶,這個比較直接了为鳄,直接使用目標(biāo)對象的方法作為線程啟動入口。

2.使用NSObject

其實NSObject直接就加入了多線程的支持腕让,允許對象的某個方法在后臺運(yùn)行孤钦。如:

[myObj?performSelectorInBackground:@selector(doSomething)?withObject:nil];

3.POSIX Thread

由于Mac和iOS都是基于Darwin系統(tǒng),Darwin系統(tǒng)的XUN內(nèi)核纯丸,是基于Mach和BSD的偏形,繼承了BSD的POSIX接口,所以可以直接使用POSIX線程的相關(guān)接口來使用線程觉鼻。

創(chuàng)建線程的接口為pthread_create俊扭,當(dāng)然在創(chuàng)建之前可以通過相關(guān)函數(shù)設(shè)置好線程的屬性。以下為POSIX線程使用簡單的例子坠陈。

//?//??main.c?//??pthread?//?//??Created?by?Lu?Kejin?on?1/27/12.?//??Copyright?(c)?2012?Taobao.com.?Al

.多線程進(jìn)階

NSOperation&NSOperationQueue

很多時候我們使用多線程萨惑,需要控制線程的并發(fā)數(shù),畢竟線程也是消耗系統(tǒng)資源的仇矾,當(dāng)程序中同時運(yùn)行的線程過多時庸蔼,系統(tǒng)必然變慢。所以很多時候我們會控制同時運(yùn)行線程的數(shù)目贮匕。

NSOperation可以封裝我們的操作姐仅,然后將創(chuàng)建好的NSOperation對象放到NSOperationQueue中,OperationQueue便開始啟動新的線程去執(zhí)行隊列中的操作刻盐,OperationQueue的并發(fā)度是可以通過如下方式進(jìn)行設(shè)置:

-?(void)setMaxConcurrentOperationCount:(NSInteger)count

GCD

GCD是Grand Central Dispatch的縮寫掏膏,是一系列的BSD層面的接口,在Mac 10.6和iOS4.0以后才引入的敦锌,且現(xiàn)在NSOperation和NSOperationQueue的多線程的實現(xiàn)就是基于GCD的馒疹。目前這個特性也被移植到FreeBSD上了,可以查看libdispatch這個開源項目乙墙。

比如一個在UIImageView中顯示一個比較大的圖片

dispatch_queue_t?imageDownloadQueue?=?dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,?0);?dispatch_async(imageDownloa

當(dāng)然颖变,GCD除了處理多線程外還有很多非常好的功能生均,其建立在強(qiáng)大的kqueue之上,效率也能夠得到保障悼做。

.線程間通信

線程間通信和進(jìn)程間通信從本質(zhì)上講是相似的。線程間通信就是在進(jìn)程內(nèi)的兩個執(zhí)行流之間進(jìn)行數(shù)據(jù)的傳遞哗魂,就像兩條并行的河流之間挖出了一道單向流動長溝肛走,使得一條河流中的水可以流入另一條河流,物質(zhì)得到了傳遞录别。

1.performSelect On The Thread

框架為我們提供了強(qiáng)制在某個線程中執(zhí)行方法的途徑,如果兩個非主線程的線程需要相互間通信朽色,可以先將自己的當(dāng)前線程對象注冊到某個全局的對象中去,這樣相互之間就可以獲取對方的線程對象组题,然后就可以使用下面的方法進(jìn)行線程間的通信了葫男,由于主線程比較特殊,所以框架直接提供了在出線程執(zhí)行的方法崔列。

@interface?NSObject?(NSThreadPerformAdditions)?-?(void)performSelectorOnMainThread:(SEL)aSelector?withObject:(id)arg?waitUnti

2.Mach Port

在蘋果的Thread Programming Guide的Run Pool一節(jié)的Configuring a Port-Based Input Source這一段中就有使用Mach Port進(jìn)行線程間通信的例子梢褐。其實質(zhì)就是父線程創(chuàng)建一個NSMachPort對象,在創(chuàng)建子線程的時候以參數(shù)的方式將其傳遞給子線程赵讯,這樣子線程中就可以向這個傳過來的NSMachPort對象發(fā)送消息盈咳,如果想讓父線程也可以向子線程發(fā)消息的話,那么子線程可以先向父線程發(fā)個特殊的消息边翼,傳過來的是自己創(chuàng)建的另一個NSMachPort對象鱼响,這樣父線程便持有了子線程創(chuàng)建的port對象了,可以向這個子線程的port對象發(fā)送消息了组底。

當(dāng)然各自的port對象需要設(shè)置delegate以及schdule到自己所在線程的RunLoop中丈积,這樣來了消息之后,處理port消息的delegate方法會被調(diào)用债鸡,你就可以自己處理消息了江滨。

.RunLoop

RunLoop從字面上看是運(yùn)行循環(huán)的意思,這一點(diǎn)也不錯厌均,它確實就是一個循環(huán)的概念牙寞,或者準(zhǔn)確的說是線程中的循環(huán)。本文一開始就提到有些程序是一個圈莫秆,這個圈本質(zhì)上就是這里的所謂的RunLoop间雀,就是一個循環(huán),只是這個循環(huán)里加入很多特性镊屎。

首先循環(huán)體的開始需要檢測是否有需要處理的事件惹挟,如果有則去處理,如果沒有則進(jìn)入睡眠以節(jié)省CPU時間缝驳。所以重點(diǎn)便是這個需要處理的事件连锯,在RunLoop中归苍,需要處理的事件分兩類,一種是輸入源运怖,一種是定時器拼弃,定時器好理解就是那些需要定時執(zhí)行的操作,輸入源分三類:performSelector源摇展,基于端口(Mach port)的源吻氧,以及自定義的源。編程的時候可以添加自己的源咏连。RunLoop還有一個觀察者Observer的概念盯孙,可以往RunLoop中加入自己的觀察者以便監(jiān)控著RunLoop的運(yùn)行過程,CFRunLoop.h中定義了所有觀察者的類型:

enum?CFRunLoopActivity?{?kCFRunLoopEntry?=?(1?<<?0),?kCFRunLoopBeforeTimers?=?(1?<<?1),?kCFRunLoopBeforeSources?=?(

如果你使用過select系統(tǒng)調(diào)用寫過程序你便可以快速的理解runloop事件源的概念祟滴,本質(zhì)上講事件源的機(jī)制和select一樣是一種多路復(fù)用IO的實現(xiàn)振惰,在一個線程中我們需要做的事情并不單一,如需要處理定時鐘事件垄懂,需要處理用戶的觸控事件骑晶,需要接受網(wǎng)絡(luò)遠(yuǎn)端發(fā)過來的數(shù)據(jù),將這些需要做的事情統(tǒng)統(tǒng)注冊到事件源中草慧,每一次循環(huán)的開始便去檢查這些事件源是否有需要處理的數(shù)據(jù)透罢,有的話則去處理。拿具體的應(yīng)用舉個例子冠蒋,NSURLConnection網(wǎng)絡(luò)數(shù)據(jù)請求羽圃,默認(rèn)是異步的方式,其實現(xiàn)原理就是創(chuàng)建之后將其作為事件源加入到當(dāng)前的RunLoop抖剿,而等待網(wǎng)絡(luò)響應(yīng)以及網(wǎng)絡(luò)數(shù)據(jù)接受的過程則在一個新創(chuàng)建的獨(dú)立的線程中完成朽寞,當(dāng)這個線程處理到某個階段的時候比如得到對方的響應(yīng)或者接受完了網(wǎng)絡(luò)數(shù)據(jù)之后便通知之前的線程去執(zhí)行其相關(guān)的delegate方法。所以在Cocoa中經(jīng)痴独桑看到scheduleInRunLoop:forMode:這樣的方法脑融,這個便是將其加入到事件源中,當(dāng)檢測到某個事件發(fā)生的時候缩宜,相關(guān)的delegate方法便被調(diào)用肘迎。對于CoreFoundation這一層而言,通常的模式是創(chuàng)建輸入源锻煌,然后將輸入源通過CFRunLoopAddSource函數(shù)加入到RunLoop中妓布,相關(guān)事件發(fā)生后,相關(guān)的回調(diào)函數(shù)會被調(diào)用宋梧。如CFSocket的使用匣沼。另外RunLoop中還有一個運(yùn)行模式的概念,每一個運(yùn)行循環(huán)必然運(yùn)行在某個模式下捂龄,而模式的存在是為了過濾事件源和觀察者的释涛,只有那些和當(dāng)前RunLoop運(yùn)行模式一致的事件源和觀察者才會被激活加叁。

每一個線程都有其對應(yīng)的RunLoop,但是默認(rèn)非主線程的RunLoop是沒有運(yùn)行的唇撬,需要為RunLoop添加至少一個事件源它匕,然后去run它。一般情況下我們是沒有必要去啟用線程的RunLoop的窖认,除非你在一個單獨(dú)的線程中需要長久的檢測某個事件豫柬。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市耀态,隨后出現(xiàn)的幾起案子轮傍,更是在濱河造成了極大的恐慌暂雹,老刑警劉巖首装,帶你破解...
    沈念sama閱讀 217,657評論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異杭跪,居然都是意外死亡仙逻,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,889評論 3 394
  • 文/潘曉璐 我一進(jìn)店門涧尿,熙熙樓的掌柜王于貴愁眉苦臉地迎上來系奉,“玉大人,你說我怎么就攤上這事姑廉∪绷粒” “怎么了?”我有些...
    開封第一講書人閱讀 164,057評論 0 354
  • 文/不壞的土叔 我叫張陵桥言,是天一觀的道長萌踱。 經(jīng)常有香客問我,道長号阿,這世上最難降的妖魔是什么并鸵? 我笑而不...
    開封第一講書人閱讀 58,509評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮扔涧,結(jié)果婚禮上园担,老公的妹妹穿的比我還像新娘。我一直安慰自己枯夜,他們只是感情好弯汰,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,562評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著湖雹,像睡著了一般蝙泼。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上劝枣,一...
    開封第一講書人閱讀 51,443評論 1 302
  • 那天汤踏,我揣著相機(jī)與錄音织鲸,去河邊找鬼。 笑死溪胶,一個胖子當(dāng)著我的面吹牛搂擦,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播哗脖,決...
    沈念sama閱讀 40,251評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼瀑踢,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了才避?” 一聲冷哼從身側(cè)響起橱夭,我...
    開封第一講書人閱讀 39,129評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎桑逝,沒想到半個月后棘劣,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,561評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡楞遏,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,779評論 3 335
  • 正文 我和宋清朗相戀三年茬暇,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片寡喝。...
    茶點(diǎn)故事閱讀 39,902評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡糙俗,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出预鬓,到底是詐尸還是另有隱情巧骚,我是刑警寧澤,帶...
    沈念sama閱讀 35,621評論 5 345
  • 正文 年R本政府宣布格二,位于F島的核電站劈彪,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏蟋定。R本人自食惡果不足惜粉臊,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,220評論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望驶兜。 院中可真熱鬧扼仲,春花似錦、人聲如沸抄淑。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,838評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽肆资。三九已至矗愧,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間郑原,已是汗流浹背唉韭。 一陣腳步聲響...
    開封第一講書人閱讀 32,971評論 1 269
  • 我被黑心中介騙來泰國打工夜涕, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人属愤。 一個月前我還...
    沈念sama閱讀 48,025評論 2 370
  • 正文 我出身青樓女器,卻偏偏與公主長得像,于是被迫代替她去往敵國和親住诸。 傳聞我的和親對象是個殘疾皇子驾胆,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,843評論 2 354

推薦閱讀更多精彩內(nèi)容

  • 前言 iPhone中的線程應(yīng)用并不是無節(jié)制的,官方給出的資料顯示iPhone OS下的主線程的堆棧大小是1M贱呐,第二...
    烏七貓閱讀 269評論 0 1
  • Run loop 剖析:Runloop 接收的輸入事件來自兩種不同的源:輸入源(intput source)和定時...
    Mitchell閱讀 12,430評論 17 111
  • 一丧诺、什么是runloop 字面意思是“消息循環(huán)、運(yùn)行循環(huán)”奄薇。它不是線程驳阎,但它和線程息息相關(guān)。一般來講惕艳,一個線程一次...
    WeiHing閱讀 8,130評論 11 111
  • iPhone中的線程應(yīng)用并不是無節(jié)制的搞隐,官方給出的資料顯示iPhone OS下的主線程的堆棧大小是1M驹愚,第二個線程...
    陳大帥閱讀 122評論 0 1
  • 本文將從以下幾個部分來介紹多線程远搪。 第一部分介紹多線程的基本原理。 第二部分介紹Run loop逢捺。 第三部分介紹多...
    曲年閱讀 1,264評論 2 14