OC基礎(chǔ)知識(shí)點(diǎn)之-多線程(一)多線程基礎(chǔ)

線程與進(jìn)程

線程

  • 1.線程是進(jìn)程的基本執(zhí)?單元钟鸵,?個(gè)進(jìn)程的所有任務(wù)都在線程中執(zhí)?
  • 2.進(jìn)程要想執(zhí)?任務(wù)晚伙,必須得有線程拾弃,進(jìn)程?少要有?條線程
  • 3.程序啟動(dòng)會(huì)默認(rèn)開啟?條線程這條線程被稱為主線程或 UI 線程

進(jìn)程

  • 1.進(jìn)程是指在系統(tǒng)中正在運(yùn)?的?個(gè)應(yīng)?程序
  • 2.每個(gè)進(jìn)程之間是獨(dú)?的丽旅,每個(gè)進(jìn)程均運(yùn)?在其專?的且受保護(hù)的內(nèi)存空間內(nèi)
  • 3.通過“活動(dòng)監(jiān)視器”可以查看 Mac 系統(tǒng)中所開啟的進(jìn)程

線程和進(jìn)程的聯(lián)系

  • 1.地址空間同?進(jìn)程的線程共享本進(jìn)程的地址空間,?進(jìn)程之間則是獨(dú)?的地址空間纺棺。
  • 2.資源擁有同?進(jìn)程內(nèi)的線程共享本進(jìn)程的資源如內(nèi)存榄笙、I/O、cpu等祷蝌,但是進(jìn)程之間的資源是獨(dú)?的茅撞。
    • 1: ?個(gè)進(jìn)程崩潰后,在保護(hù)模式下不會(huì)對(duì)其他進(jìn)程產(chǎn)?影響巨朦,但是?個(gè)線程崩潰整個(gè)進(jìn)程都死掉米丘。所以多進(jìn)程要?多線程健壯
    • 2: 進(jìn)程切換時(shí)罪郊,消耗的資源?蠕蚜,效率?。所以涉及到頻繁的切換時(shí)悔橄,使?線程要好于進(jìn)程靶累。同樣如果要求同時(shí)進(jìn)?并且?要共享某些變量的并發(fā)操作腺毫,只能?線程不能?進(jìn)程
    • 3: 執(zhí)?過程:每個(gè)獨(dú)?的進(jìn)程有?個(gè)程序運(yùn)?的??、順序執(zhí)?序列和程序??挣柬。但是線程不能獨(dú)?執(zhí)?潮酒,必須依存在應(yīng)?程序中,由應(yīng)?程序提供多個(gè)線程執(zhí)?控制邪蛔。
    • 4: 線程是處理器調(diào)度的基本單位急黎,但是進(jìn)程不是
    • 5: 線程沒有地址空間,線程包含在進(jìn)程地址空間中侧到。

多線程的優(yōu)點(diǎn)和缺點(diǎn)

優(yōu)點(diǎn)

    1. 能適當(dāng)提?程序的執(zhí)?效率
    1. 能適當(dāng)提?資源的利?率(CPU勃教,內(nèi)存)
    1. 線程上的任務(wù)執(zhí)?完成后,線程會(huì)?動(dòng)銷毀

缺點(diǎn)

    1. 開啟線程需要占??定的內(nèi)存空間(默認(rèn)情況下匠抗,每?個(gè)線程都占 512 KB)
    1. 如果開啟?量的線程故源,會(huì)占??量的內(nèi)存空間,降低程序的性能
  • 3.線程越多汞贸,CPU 在調(diào)?線程上的開銷就越?
  • 4.程序設(shè)計(jì)更加復(fù)雜绳军,?如線程間的通信、多線程的數(shù)據(jù)共享

多線程的實(shí)際實(shí)現(xiàn)

先介紹一個(gè)概念:時(shí)間片矢腻。時(shí)間片:CPU在多個(gè)任務(wù)之間進(jìn)行快速切換门驾,這個(gè)時(shí)間間隔就是時(shí)間片。

單核和多核

  • 1.單核:同一時(shí)間多柑,CPU只能處理一個(gè)線程奶是,也就是說同一時(shí)間只有一個(gè)線程在執(zhí)行。
  • 2.多線程同時(shí)執(zhí)行:CPU快速的在多個(gè)線程之間切換顷蟆。CPU調(diào)度線程的時(shí)間足夠快诫隅,就造成了多線程'同時(shí)'執(zhí)行的效果
  • 3.線程非常多:CPU會(huì)在N個(gè)線程之間切換,消耗大量的CPU資源帐偎,每個(gè)線程的被調(diào)度次數(shù)會(huì)降低逐纬,線程的執(zhí)行效率降低

多線程的內(nèi)存消耗

官方文檔對(duì)多線程的內(nèi)存消耗有個(gè)說明官方文檔傳送門

  • 1.線程內(nèi)核數(shù)據(jù)結(jié)構(gòu)大約占1KB,主要用于存儲(chǔ)線程數(shù)據(jù)結(jié)構(gòu)和屬性削樊,其中大部分是作為連接內(nèi)存分配的豁生,因此不能分頁到磁盤
  • 2.棧的控件大新辍:子線程512kb甸箱,OS X的主線程是8MB,iOS的主線程是1MB迅脐。子線程允許的最小堆棧大小是16 KB芍殖,堆棧大小必須是4 KB的倍數(shù)。該內(nèi)存的空間是在線程創(chuàng)建時(shí)在進(jìn)程空間中留出的谴蔑,但是只有在需要時(shí)才創(chuàng)建與該內(nèi)存關(guān)聯(lián)的實(shí)際頁面豌骏。
  • 3.創(chuàng)建時(shí)間大概90微秒龟梦,這個(gè)值反映了從創(chuàng)建線程的初始調(diào)用到線程入口點(diǎn)例程開始執(zhí)行的時(shí)間之間的時(shí)間。這些數(shù)字是通過分析在運(yùn)行OS X v10.5窃躲、配置2 GHz雙核處理器和1 GB RAM计贰、基于intel的iMac上創(chuàng)建線程時(shí)生成的平均值和中值得出的。

多線程處理方案

線程生命周期

線程池的原理

飽和策略

  • 1.AbortPolicy 直接拋出RejectedExecutionExeception異常來阻?系統(tǒng)正常運(yùn)?
  • 2.CallerRunsPolicy 將任務(wù)回退到調(diào)?者
  • 3.DisOldestPolicy 丟掉等待最久的任務(wù)
  • 4.DisCardPolicy 直接丟棄任務(wù)

四種拒絕策略均實(shí)現(xiàn)了RejectedExecutionHandler接?

線程優(yōu)先級(jí)

地址越高蒂窒,其優(yōu)先級(jí)越高躁倒,也就是說用戶操作行為的優(yōu)先級(jí)是最高的。但注意:

  • 1.優(yōu)先級(jí)越高洒琢,執(zhí)行速度不一定越快秧秉,跟資源大小(任務(wù)復(fù)雜度)和CPU的調(diào)度(多任務(wù))
  • 2.多任務(wù)就會(huì)出現(xiàn)資源搶奪問題(會(huì)導(dǎo)致數(shù)據(jù)出錯(cuò)),此時(shí)需要鎖來防止這種情況出現(xiàn)

鎖作用

  • 1.保證鎖內(nèi)的代碼衰抑,同?時(shí)間福贞,只有?條線程能夠執(zhí)?
  • 2.鎖的鎖定范圍停士,應(yīng)該盡量?,鎖定范圍越?完丽,效率越差恋技!

鎖使用的注意點(diǎn)

  • 1.能夠加鎖的任意 NSObject 對(duì)象
  • 2.注意:鎖對(duì)象?定要保證所有的線程都能夠訪問
  • 3.如果代碼中只有?個(gè)地?需要加鎖,?多都使? self逻族,這樣可以避免單獨(dú)再創(chuàng)建?個(gè)鎖對(duì)象

互斥鎖和自旋鎖

自旋鎖

自旋鎖成員

自旋鎖包含:atomic, OSSpinLock, dispatch_semaphore_t

定義:

定義:是一種用于保護(hù)多線程共享資源的鎖蜻底,與一般互斥鎖(mutex)不同之處在于當(dāng)自旋鎖嘗試獲取鎖時(shí)以忙等待(busy waiting)的形式不斷地循環(huán)檢查鎖是否可用。當(dāng)上一個(gè)線程的任務(wù)沒有執(zhí)行完畢的時(shí)候(被鎖住)聘鳞,那么下一個(gè)線程會(huì)一直等待(不會(huì)睡眠)薄辅,當(dāng)上一個(gè)線程的任務(wù)執(zhí)行完畢,下一個(gè)線程會(huì)立即執(zhí)行抠璃。

自旋鎖會(huì)忙等: 所謂忙等站楚,即在訪問被鎖資源時(shí),調(diào)用者線程不會(huì)休眠搏嗡,而是不停循環(huán)在那里窿春,直到被鎖資源釋放鎖。

使用場(chǎng)景

  • 1.對(duì)持有鎖較短的程序來說采盒,使用自旋鎖代替一般的互斥鎖往往能夠提高程序的性能旧乞。
  • 2.加鎖代碼(臨界區(qū))經(jīng)常被調(diào)用,但競(jìng)爭(zhēng)情況很少發(fā)生
  • 3.CPU資源不緊張
  • 4.多核處理器

互斥鎖

互斥鎖成員

互斥鎖包含:@synchronized磅氨,NSLock, pthread_mutex, NSConditionLock, NSCondition, NSRecursiveLock

定義

定義:互斥鎖當(dāng)上一個(gè)線程的任務(wù)沒有執(zhí)行完畢的時(shí)候(被鎖壮咂堋),那么下一個(gè)線程會(huì)進(jìn)入睡眠狀態(tài)等待任務(wù)執(zhí)行完畢烦租,當(dāng)上一個(gè)線程的任務(wù)執(zhí)行完畢延赌,下一個(gè)線程會(huì)自動(dòng)喚醒然后執(zhí)行任務(wù)除盏。

互斥鎖會(huì)休眠: 所謂休眠,即在訪問被鎖資源時(shí)皮胡,調(diào)用者線程會(huì)休眠痴颊,此時(shí)cpu可以調(diào)度其他線程工作。直到被鎖資源釋放鎖屡贺。此時(shí)會(huì)喚醒休眠線程蠢棱。

使用場(chǎng)景

  • 1.預(yù)計(jì)線程等待鎖的時(shí)間較長(zhǎng)
  • 2.單核處理器
  • 3.臨界區(qū)有IO操作
  • 4.臨界區(qū)代碼復(fù)雜或者循環(huán)量大
  • 5.臨界區(qū)競(jìng)爭(zhēng)非常激烈

atomic與nonatomic 的區(qū)別

nonatomic ?原?屬性

  • 1.nonatomic:?線程安全,適合內(nèi)存?的移動(dòng)設(shè)備

atomic 原?屬性

  • 1.讀取值線程安全
  • 2..atomic:讀寫安全甩栈,需要消耗?量的資源(至少是nonatomic的10倍)

說明:使用atomic修飾屬性時(shí)泻仙,編譯器在生成getter和setter方法時(shí),在getter和setter方法內(nèi)部實(shí)現(xiàn)進(jìn)行加鎖操作量没,這么做的目的是為了保證屬性讀寫的安全性和完整性玉转,也就是說對(duì)于屬性值得存取是線程安全的。但這個(gè)不能保證操作這個(gè)屬性的時(shí)候是線程安全的殴蹄。

iOS 開發(fā)的建議

  • 1.所有屬性都聲明為 nonatomic
  • 2.盡量避免多線程搶奪同?塊資源究抓,盡量將加鎖、資源搶奪的業(yè)務(wù)邏輯交給服務(wù)器端處理袭灯,減?移動(dòng)客戶端的壓?

線程和Runloop的關(guān)系

  • 1.runloop與線程是??對(duì)應(yīng)的刺下,?個(gè)runloop對(duì)應(yīng)?個(gè)核?的線程,為什么說是核?的稽荧,是因?yàn)?code>runloop是可以嵌套的橘茉,但是核?的只能有?個(gè),他們的關(guān)系保存在?個(gè)全局的字典?姨丈。
  • 2.runloop是來管理線程的畅卓,當(dāng)線程的runloop被開啟后,線程會(huì)在執(zhí)?完任務(wù)后進(jìn)?休眠狀態(tài)蟋恬,有了任務(wù)就會(huì)被喚醒去執(zhí)?任務(wù)翁潘。
  • 3.runloop在第?次獲取時(shí)被創(chuàng)建,在線程結(jié)束時(shí)被銷毀筋现。
  • 4.對(duì)于主線程來說唐础,runloop在程序?啟動(dòng)就默認(rèn)創(chuàng)建好了
  • 5.對(duì)于?線程來說矾飞,runloop是懶加載的一膨,只有當(dāng)我們使?的時(shí)候才會(huì)創(chuàng)建,所以在?線程?定時(shí)器要注意:確保?線程的runloop被創(chuàng)建洒沦,不然定時(shí)器不會(huì)回調(diào)豹绪。

延伸

端口通訊:NSPort

下面我們舉例子來展示多線程的端口通訊,通過KCPerson對(duì)象跟ViewController進(jìn)行發(fā)送消息的例子

// ViewController.m
    //1\. 創(chuàng)建主線程的port
    // 子線程通過此端口發(fā)送消息給主線程
    self.myPort = [NSMachPort port];
    //2\. 設(shè)置port的代理回調(diào)對(duì)象
    self.myPort.delegate = self;
    //3\. 把port加入runloop,接收port消息
    [[NSRunLoop currentRunLoop] addPort:self.myPort forMode:NSDefaultRunLoopMode];
    self.person = [[KCPerson alloc] init];
    [NSThread detachNewThreadSelector:@selector(personLaunchThreadWithPort:)
                             toTarget:self.person
                           withObject:self.myPort];

- (void)handlePortMessage:(NSPortMessage *)message {
    NSLog(@"VC == %@",[NSThread currentThread]);
    NSArray *messageArr = [message valueForKey:@"components"];
    NSString *dataStr   = [[NSString alloc] initWithData:messageArr.firstObject  encoding:NSUTF8StringEncoding];
    NSLog(@"傳過來一些信息 :%@",dataStr);
    NSPort  *destinPort = [message valueForKey:@"remotePort"];
    if(!destinPort || ![destinPort isKindOfClass:[NSPort class]]){
        NSLog(@"傳過來的數(shù)據(jù)有誤");
        return;
    }
    NSData *data = [@"VC收到!!!" dataUsingEncoding:NSUTF8StringEncoding];
    NSMutableArray *array  =[[NSMutableArray alloc]initWithArray:@[data,self.myPort]];
    // 非常重要,如果你想在Person的port接受信息,必須加入到當(dāng)前主線程的runloop
    [[NSRunLoop currentRunLoop] addPort:destinPort forMode:NSDefaultRunLoopMode];

    NSLog(@"VC == %@",[NSThread currentThread]);

    BOOL success = [destinPort sendBeforeDate:[NSDate date]
                                        msgid:10010
                                   components:array
                                         from:self.myPort
                                     reserved:0];
    NSLog(@"%d",success);

}
// Person.m
- (void)personLaunchThreadWithPort:(NSPort *)port{

    NSLog(@"VC 響應(yīng)了Person里面");
    @autoreleasepool {
        //1\. 保存主線程傳入的port
        self.vcPort = port;
        //2\. 設(shè)置子線程名字
        [[NSThread currentThread] setName:@"KCPersonThread"];
        //3\. 開啟runloop
        [[NSRunLoop currentRunLoop] run];
        //4\. 創(chuàng)建自己port
        self.myPort = [NSMachPort port];
        //5\. 設(shè)置port的代理回調(diào)對(duì)象
        self.myPort.delegate = self;
        //6\. 完成向主線程port發(fā)送消息
        [self sendPortMessage];
    }
}
/**
 *   完成向主線程發(fā)送port消息
 */

- (void)sendPortMessage {
    NSData *data1 = [@"AAAA" dataUsingEncoding:NSUTF8StringEncoding];
    NSData *data2 = [@"BBBB" dataUsingEncoding:NSUTF8StringEncoding];
    NSMutableArray *array  =[[NSMutableArray alloc]initWithArray:@[data1,self.myPort]];
    // 發(fā)送消息到VC的主線程
    // 第一個(gè)參數(shù):發(fā)送時(shí)間瞒津。
    // msgid 消息標(biāo)識(shí)蝉衣。
    // components,發(fā)送消息附帶參數(shù)巷蚪。
    // reserved:為頭部預(yù)留的字節(jié)數(shù)
    [self.vcPort sendBeforeDate:[NSDate date]
                          msgid:10086
                     components:array
                           from:self.myPort
                       reserved:0];
}

- (void)handlePortMessage:(NSPortMessage *)message{

    NSLog(@"person:handlePortMessage  == %@",[NSThread currentThread]);

    NSLog(@"從VC 傳過來一些信息:");
    NSLog(@"components == %@",[message valueForKey:@"components"]);
    NSLog(@"receivePort == %@",[message valueForKey:@"receivePort"]);
    NSLog(@"sendPort == %@",[message valueForKey:@"sendPort"]);
    NSLog(@"msgid == %@",[message valueForKey:@"msgid"]);
}

運(yùn)行打硬≌薄:

注意:

  • 1.NSPort對(duì)象必須添加到要接受的線程的runLoop中
  • 2.接收消息的對(duì)象實(shí)現(xiàn)NSPortDelegate協(xié)議的-handlePortMessage:方法來獲取消息內(nèi)容
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市屁柏,隨后出現(xiàn)的幾起案子啦膜,更是在濱河造成了極大的恐慌,老刑警劉巖淌喻,帶你破解...
    沈念sama閱讀 219,366評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件僧家,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡裸删,警方通過查閱死者的電腦和手機(jī)八拱,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,521評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來涯塔,“玉大人肌稻,你說我怎么就攤上這事∝拜” “怎么了灯萍?”我有些...
    開封第一講書人閱讀 165,689評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)每聪。 經(jīng)常有香客問我,道長(zhǎng)齿风,這世上最難降的妖魔是什么药薯? 我笑而不...
    開封第一講書人閱讀 58,925評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮救斑,結(jié)果婚禮上童本,老公的妹妹穿的比我還像新娘。我一直安慰自己脸候,他們只是感情好穷娱,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,942評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著运沦,像睡著了一般泵额。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上携添,一...
    開封第一講書人閱讀 51,727評(píng)論 1 305
  • 那天嫁盲,我揣著相機(jī)與錄音,去河邊找鬼烈掠。 笑死羞秤,一個(gè)胖子當(dāng)著我的面吹牛缸托,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播瘾蛋,決...
    沈念sama閱讀 40,447評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼俐镐,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了哺哼?” 一聲冷哼從身側(cè)響起佩抹,我...
    開封第一講書人閱讀 39,349評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎幸斥,沒想到半個(gè)月后匹摇,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,820評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡甲葬,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,990評(píng)論 3 337
  • 正文 我和宋清朗相戀三年廊勃,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片经窖。...
    茶點(diǎn)故事閱讀 40,127評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡坡垫,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出画侣,到底是詐尸還是另有隱情冰悠,我是刑警寧澤,帶...
    沈念sama閱讀 35,812評(píng)論 5 346
  • 正文 年R本政府宣布配乱,位于F島的核電站溉卓,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏搬泥。R本人自食惡果不足惜桑寨,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,471評(píng)論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望忿檩。 院中可真熱鬧尉尾,春花似錦、人聲如沸燥透。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,017評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽班套。三九已至肢藐,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間吱韭,已是汗流浹背窖壕。 一陣腳步聲響...
    開封第一講書人閱讀 33,142評(píng)論 1 272
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人瞻讽。 一個(gè)月前我還...
    沈念sama閱讀 48,388評(píng)論 3 373
  • 正文 我出身青樓鸳吸,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親速勇。 傳聞我的和親對(duì)象是個(gè)殘疾皇子晌砾,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,066評(píng)論 2 355

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