多線程(一)

線程與進(jìn)程

  • 線程是進(jìn)程的基本執(zhí)行單元,一個(gè)進(jìn)程所有任務(wù)都在線程中執(zhí)行

  • 進(jìn)程想要執(zhí)行任務(wù),必須得有線程,進(jìn)程至少要有一條線程

  • 程序啟動(dòng)會(huì)默認(rèn)開啟一條線程,這條線程被稱為主線程或UI線程

  • 進(jìn)程實(shí)質(zhì)在系統(tǒng)中正在運(yùn)行的一個(gè)應(yīng)用程序

  • 每個(gè)進(jìn)程之前是獨(dú)立的,每個(gè)進(jìn)程均運(yùn)行在其專用的且受保護(hù)的內(nèi)存空間內(nèi)

  • 通過(guò)“活動(dòng)監(jiān)視器”可以查看Mac系統(tǒng)中所開啟的進(jìn)程

進(jìn)程與線程的關(guān)系

地址空間:同一進(jìn)程的線程共享本進(jìn)程的地址空間,而進(jìn)程之間則是獨(dú)立的地址空間
資源擁有:同一進(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í)行過(guò)程:每個(gè)獨(dú)立的進(jìn)程有一個(gè)程序運(yùn)行的入口、順序執(zhí)行序列和程序入口。但是線程不能獨(dú)立執(zhí)行,必須已存在應(yīng)用程序中,由應(yīng)用程序提供多個(gè)線程執(zhí)行控制。
  4. 線程是處理器調(diào)度的基本單位,但進(jìn)程不是。
  5. 線程沒有地址空間,線程包含在進(jìn)程地址空間中。

時(shí)間片的概念:CPU在多個(gè)任務(wù)直接進(jìn)行快速的切換,這個(gè)時(shí)間間隔就是時(shí)間片

  • 單核CPU同一時(shí)間,只能處理一個(gè)線程
  • 多線程同時(shí)執(zhí)行其實(shí)是CPU快速的在多個(gè)線程之前切換,CPU調(diào)度線程的時(shí)間足夠快,就造成了多線程的“同時(shí)”執(zhí)行的效果
  • 如果線程數(shù)非常多,CPU會(huì)在n個(gè)線程之間切換,消耗大量的CPU資源,導(dǎo)致每個(gè)線程唄調(diào)度的次數(shù)會(huì)降低,線程的執(zhí)行效率降低
線程生命周期
可調(diào)度線程池

飽和策略:

  • AbortPolicy 直接拋出RejectedExecutionExeception異常來(lái)阻止系統(tǒng)正常運(yùn)行
  • CallerRunsPolicy 將任務(wù)退回到調(diào)度者
  • DisOldestPolicy 丟掉等待最久的任務(wù)
  • DisCardPolicy 直接丟棄任務(wù)

這四種拒絕策略均實(shí)現(xiàn)在RejectedExecutionHandler接口

任務(wù)執(zhí)行速度的影響因素

  1. cpu狀態(tài)
  2. 任務(wù)復(fù)雜度
  3. 優(yōu)先級(jí)
  4. 線程狀態(tài)

優(yōu)先級(jí)反轉(zhuǎn):

  1. IO 密集型,頻繁等待
  2. CPU 密集型,很少等待
    IO比CPU更容易得到優(yōu)先級(jí)提升

優(yōu)先級(jí)影響因素:

  1. 用戶指定
  2. 等待的頻繁度,頻繁調(diào)用優(yōu)先級(jí)會(huì)降低
  3. 長(zhǎng)時(shí)間不執(zhí)行,會(huì)提升優(yōu)先級(jí)

互斥鎖 發(fā)現(xiàn)其他線程執(zhí)行 當(dāng)前線程 休眠 (就緒狀態(tài)) 一直在等打開 喚醒執(zhí)行
自旋鎖 發(fā)現(xiàn)其他線程執(zhí)行 當(dāng)前線程 詢問(wèn) - 忙等 耗費(fèi)性能比較高

在短小精悍的項(xiàng)目里用自旋鎖,或者環(huán)境資源充足,比如mac環(huán)境,用自旋鎖

atomic 是原子屬性,是為多線程開發(fā)準(zhǔn)備的,是默認(rèn)屬性!
僅僅在屬性的 setter 方法中,增加了鎖(自旋鎖),能夠保證同一時(shí)間,只有一條線程對(duì)屬性進(jìn)行寫操作
同一時(shí)間 單(線程)寫多(線程)讀的線程處理技術(shù)
nonatomic 是非原子屬性
沒有鎖!性能高!

在源碼中可以看到

if (!atomic) {
    oldValue = *slot;
    *slot = newValue;
} else {
    spinlock_t& slotlock = PropertyLocks[slot];
    slotlock.lock();
    oldValue = *slot;
    *slot = newValue;        
    slotlock.unlock();
}

下面看個(gè)面試題:

dispatch_queue_t queue = dispatch_queue_create("sj", DISPATCH_QUEUE_CONCURRENT);
NSLog(@"1");
dispatch_async(queue, ^{
    NSLog(@"2");
    dispatch_sync(queue, ^{
        NSLog(@"3");
    });
    NSLog(@"4");
});
NSLog(@"5");

輸出結(jié)果順序是:1 5 2 3 4

image

再看下面一段代碼:

dispatch_queue_t queue = dispatch_queue_create("sj", DISPATCH_QUEUE_SERIAL);
NSLog(@"1");
dispatch_async(queue, ^{
    NSLog(@"2");
    dispatch_sync(queue, ^{
        NSLog(@"3");
    });
    NSLog(@"4");
});
NSLog(@"5");

輸出結(jié)果是: 1 5 2 崩潰

image

這個(gè)會(huì)造成死鎖。第二個(gè)塊會(huì)往第一個(gè)塊后面增加一個(gè)任務(wù),第二個(gè)塊任務(wù)不執(zhí)行完,是執(zhí)行不了第一個(gè)塊下面的代碼,但是第一個(gè)塊代碼不執(zhí)行完,又不能執(zhí)行第二個(gè)塊里面的代碼,相互等待造成死鎖。

再看一種情況:

dispatch_queue_t queue = dispatch_queue_create("sj", DISPATCH_QUEUE_SERIAL);
NSLog(@"1");
dispatch_async(queue, ^{
    NSLog(@"2");
    dispatch_async(queue, ^{
        NSLog(@"3");
    });
    NSLog(@"4");
});
NSLog(@"5");

輸出結(jié)果是: 1 5 2 4 3

image
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容