iOS——多線程理解

首先先說(shuō)明此文是學(xué)習(xí)了李峰峰大牛的博客后所寫(xiě),有興趣的可以百度搜索一下李峰峰的博客。

一、線程和進(jìn)程

1、線程

線程,是程序執(zhí)行流的最小單元,線程是程序中一個(gè)單一的順序控制流程。是進(jìn)程內(nèi)一個(gè)相對(duì)獨(dú)立的、可調(diào)度的執(zhí)行單元,是系統(tǒng)獨(dú)立調(diào)度和分派CPU的基本單位,指運(yùn)行中的程序的調(diào)度單位。

簡(jiǎn)單來(lái)說(shuō),1個(gè)進(jìn)程要想執(zhí)行任務(wù),必須得有線程。

線程中任務(wù)的執(zhí)行是串行的,要在1個(gè)線程中執(zhí)行多個(gè)任務(wù),那么只能一個(gè)一個(gè)地按順序執(zhí)行這些任務(wù),也就是說(shuō),在同一時(shí)間內(nèi),1個(gè)線程只能執(zhí)行1個(gè)任務(wù),由此可以理解線程是進(jìn)程中的1條執(zhí)行路徑。

一個(gè)進(jìn)程中至少包含一條線程,即主線程,創(chuàng)建線程的目的就是為了開(kāi)啟一條新的執(zhí)行路徑,運(yùn)行指定的代碼,與主線程中的代碼實(shí)現(xiàn)同時(shí)運(yùn)行。

2、進(jìn)程

進(jìn)程(Process)是計(jì)算機(jī)中的程序關(guān)于某數(shù)據(jù)集合上的一次運(yùn)行活動(dòng),是系統(tǒng)進(jìn)行資源分配和調(diào)度的基本單位,是操作系統(tǒng)結(jié)構(gòu)的基礎(chǔ),每一個(gè)進(jìn)程都有自己獨(dú)立的虛擬內(nèi)存空間。

簡(jiǎn)單來(lái)說(shuō),進(jìn)程是指在系統(tǒng)中正在運(yùn)行的一個(gè)應(yīng)用程序,每一個(gè)程序都是一個(gè)進(jìn)程,并且進(jìn)程之間是獨(dú)立的,每個(gè)進(jìn)程均運(yùn)行在其專用且受保護(hù)的內(nèi)存空間內(nèi)。

二、多線程

1.多線程簡(jiǎn)介

多線程(multithreading),是指從軟件或者硬件上實(shí)現(xiàn)多個(gè)線程并發(fā)執(zhí)行的技術(shù)。具有多線程能力的計(jì)算機(jī)因有硬件支持而能夠在同一時(shí)間執(zhí)行多于一個(gè)線程,進(jìn)而提升整體處理性能。

原理:

  • 同一時(shí)間,CPU只能處理1條線程,只有1條線程在工作(執(zhí)行)
  • 多線程并發(fā)(同時(shí))執(zhí)行,其實(shí)是CPU快速地在多條線程之間調(diào)度(切換)
  • 如果CPU調(diào)度線程的時(shí)間足夠快,就造成了多線程并發(fā)執(zhí)行的假象
    注意:多線程并發(fā),并不是cpu在同一時(shí)刻同時(shí)執(zhí)行多個(gè)任務(wù),只是CPU調(diào)度足夠快,造成的假象。

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

  • 能適當(dāng)提高程序的執(zhí)行效率
  • 能適當(dāng)提高資源利用率(CPU、內(nèi)存利用率)

缺點(diǎn):

  • 開(kāi)啟線程需要占用一定的內(nèi)存空間(默認(rèn)情況下,主線程占用1M,子線程占用512KB),如果開(kāi)啟大量的線程,會(huì)占用大量的內(nèi)存空間,降低程序的性能
  • 線程越多,CPU在調(diào)度線程上的開(kāi)銷就越大

主線程棧區(qū)的1M,非常非常寶貴。一個(gè)進(jìn)程,至少有一個(gè)線程(主線程),不能殺掉一個(gè)線程!但是可以暫停、休眠。

2.iOS中多線程的實(shí)現(xiàn)方式

  • NSThread:

(1)使用NSThread對(duì)象建立一個(gè)線程非常方便

(2)但是!要使用NSThread管理多個(gè)線程非常困難,不推薦使用

(3)技巧!使用[NSThread currentThread]獲得任務(wù)所在線程,適用于這三種技術(shù)

(4)使線程休眠3秒:[NSThread sleepForTimeInterval:0.3f];

  • GCD —— Grand Central Dispatch:

(1)是基于C語(yǔ)言的底層API

(2)用Block定義任務(wù),使用起來(lái)非常靈活便捷

(3)提供了更多的控制能力以及操作隊(duì)列中所不能使用的底層函數(shù)

  • NSOperation/NSOperationQueue:

(1)是使用GCD實(shí)現(xiàn)的一套Objective-C的API

(2)是面向?qū)ο蟮木€程技術(shù)

(3)提供了一些在GCD中不容易實(shí)現(xiàn)的特性,如:限制最大并發(fā)數(shù)量、操作之間的依賴關(guān)系

3.多線程的安全問(wèn)題

多個(gè)線程訪問(wèn)同一塊資源進(jìn)行讀寫(xiě),如果不加控制隨意訪問(wèn)容易產(chǎn)生數(shù)據(jù)錯(cuò)亂從而引發(fā)數(shù)據(jù)安全問(wèn)題。為了解決這一問(wèn)題,就有了加鎖的概念。加鎖的原理就是當(dāng)有一個(gè)線程正在訪問(wèn)資源進(jìn)行寫(xiě)的時(shí)候,不允許其他線程再訪問(wèn)該資源,只有當(dāng)該線程訪問(wèn)結(jié)束后,其他線程才能按順序進(jìn)行訪問(wèn)。對(duì)于讀取數(shù)據(jù),有些程序設(shè)計(jì)是允許多線程同時(shí)讀的,有些不允許。UIKit中幾乎所有控件都不是線程安全的,因此需要在主線程上更新UI。

解決多線程安全問(wèn)題:

(1)互斥鎖

// 注意:鎖定1份代碼只用1把鎖,用多把鎖是無(wú)效的
@synchronized(鎖對(duì)象) { // 需要鎖定的代碼  }

使用互斥鎖,在同一個(gè)時(shí)間,只允許一條線程執(zhí)行鎖中的代碼。因?yàn)榛コ怄i的代價(jià)非常昂貴,所以鎖定的代碼范圍應(yīng)該盡可能小,只要鎖住資源讀寫(xiě)部分的代碼即可。使用互斥鎖也會(huì)影響并發(fā)的目的。

(2)使用NSLock對(duì)象

_lock = [[NSLock alloc] init];
 - (void)synchronizedMethod {
    [_lock lock];
    //safe
    [_lock unlock];
 }

(3)atomic加鎖,加鎖過(guò)程系統(tǒng)已自動(dòng)完成

OC在定義屬性時(shí)有nonatomic和atomic兩種選擇。

atomic:原子屬性,為setter方法加鎖(默認(rèn)就是atomic)。

nonatomic:非原子屬性,不會(huì)為setter方法加鎖。

atomic加鎖原理:

 @property (assign, atomic) int age;
 
 - (void)setAge:(int)age
 { 
 
     @synchronized(self) { 
        _age = age;
     }
 }

atomic:線程安全,需要消耗大量的資源

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

iOS開(kāi)發(fā)的建議:

(1)所有屬性都聲明為nonatomic

(2)盡量避免多線程搶奪同一塊資源

(3)盡量將加鎖、資源搶奪的業(yè)務(wù)邏輯交給服務(wù)器端處理,減小移動(dòng)客戶端的壓力

4.多線程間的通信

//在主線程上執(zhí)行操作,例如給UIImageVIew設(shè)置圖片
- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)arg waitUntilDone:(BOOL)wait
//在指定線程上執(zhí)行操作
- (void)performSelector:(SEL)aSelector onThread:(NSThread *)thread withObject:(id)arg waitUntilDone:(BOOL)wait

三、GCD的介紹

GCD(Grand Central Dispatch) 偉大的中央調(diào)度系統(tǒng),是蘋(píng)果為多核并行運(yùn)算提出的C語(yǔ)言并發(fā)技術(shù)框架。

GCD會(huì)自動(dòng)利用更多的CPU內(nèi)核;
會(huì)自動(dòng)管理線程的生命周期(創(chuàng)建線程,調(diào)度任務(wù),銷毀線程等);
程序員只需要告訴 GCD 想要如何執(zhí)行什么任務(wù),不需要編寫(xiě)任何線程管理代碼。

  • 多線程中的一些概念
  • dispatch :派遣/調(diào)度
  • queue:隊(duì)列——用來(lái)存放任務(wù)的先進(jìn)先出(FIFO)的容器
  • sync:同步——只是在當(dāng)前線程中執(zhí)行任務(wù),不具備開(kāi)啟新線程的能力
  • async:異步——可以在新的線程中執(zhí)行任務(wù),具備開(kāi)啟新線程的能力
  • concurrent:并發(fā)——指一個(gè)處理器,宏觀上同時(shí)處理多個(gè)任務(wù)。
  • 串行:一個(gè)任務(wù)執(zhí)行完畢后,再執(zhí)行下一個(gè)任務(wù)
  • 并行:指多個(gè)處理器或者是多核的處理器同時(shí)處理多個(gè)不同的任務(wù)。
并行和并發(fā)的詳細(xì)介紹:
  • 并行(parallel):指在同一時(shí)刻,有多條指令在多個(gè)處理器上同時(shí)執(zhí)行。就好像兩個(gè)人各拿一把鐵锨在挖坑,一小時(shí)后,每人一個(gè)大坑。所以無(wú)論從微觀還是從宏觀來(lái)看,二者都是一起執(zhí)行的。
  • 并發(fā)(concurrency):指在同一時(shí)刻只能有一條指令執(zhí)行,但多個(gè)進(jìn)程指令被快速的輪換執(zhí)行,使得在宏觀上具有多個(gè)進(jìn)程同時(shí)執(zhí)行的效果,但在微觀上并不是同時(shí)執(zhí)行的,只是把時(shí)間分成若干段,使多個(gè)進(jìn)程快速交替的執(zhí)行。這就好像兩個(gè)人用同一把鐵锨,輪流挖坑,一小時(shí)后,兩個(gè)人各挖一個(gè)小一點(diǎn)的坑,要想挖兩個(gè)大一點(diǎn)得坑,一定會(huì)用兩個(gè)小時(shí)。
sync和async的詳細(xì)介紹:
  • dispatch_sync()——同步添加操作。他是等待添加進(jìn)隊(duì)列里面的操作完成之后再繼續(xù)執(zhí)行。
dispatch_queue_t concurrentQueue = dispatch_queue_create("my.concurrent.queue", DISPATCH_QUEUE_CONCURRENT);
    NSLog(@"1");
    dispatch_sync(concurrentQueue, ^(){
        NSLog(@"2");
        [NSThread sleepForTimeInterval:10];
        NSLog(@"3");
    });
    NSLog(@"4");

//控制臺(tái)打印
11:36:25.313 GCDSeTest[544:303] 1
11:36:25.313 GCDSeTest[544:303] 2
11:36:30.313 GCDSeTest[544:303] 3//模擬長(zhǎng)時(shí)間操作
11:36:30.314 GCDSeTest[544:303] 4

  • dispatch_async ,異步添加進(jìn)任務(wù)隊(duì)列,它不會(huì)做任何等待
dispatch_queue_t concurrentQueue = dispatch_queue_create("my.concurrent.queue", DISPATCH_QUEUE_CONCURRENT);
    NSLog(@"1");
    dispatch_async(concurrentQueue, ^(){
        NSLog(@"2");
        [NSThread sleepForTimeInterval:5];
        NSLog(@"3");
    });
    NSLog(@"4");

//控制臺(tái)打印
11:42:43.820 GCDSeTest[568:303] 1
11:42:43.820 GCDSeTest[568:303] 4
11:42:43.820 GCDSeTest[568:1003] 2
11:42:48.821 GCDSeTest[568:1003] 3//模擬長(zhǎng)時(shí)間操作時(shí)間

  • 線程死鎖
- (void)viewDidLoad
{
    [super viewDidLoad];
    NSLog(@"=================4");
    dispatch_sync(dispatch_get_main_queue(), ^{
        NSLog(@"=================5");
    });
    NSLog(@"=================6");
}

dispatch_async(queue,block) async 異步隊(duì)列,dispatch_async 函數(shù)會(huì)立即返回, block會(huì)在后臺(tái)異步執(zhí)行。

dispatch_sync(queue,block) sync 同步隊(duì)列,dispatch_sync 函數(shù)不會(huì)立即返回,及阻塞當(dāng)前線程,等待 block同步執(zhí)行完成。

分析上面代碼:

viewDidLoad 在主線程中, 及在
dispatch_get_main_queue() 中,執(zhí)行到sync 時(shí) 向
dispatch_get_main_queue()插入 同步 threed1.

sync 會(huì)等到 后面block 執(zhí)行完成才返回, sync 又再 dispatch_get_main_queue() 隊(duì)列中,
它是串行隊(duì)列,sync 是后加入的,前一個(gè)是主線程,
所以 sync 想執(zhí)行 block 必須等待主線程執(zhí)行完成,主線程等待 sync 返回,去執(zhí)行后續(xù)內(nèi)容。

造成死鎖,sync 等待mainThread 執(zhí)行完成, mianThread 等待sync 函數(shù)返回。

  • 這種情況又不會(huì)造成線程死鎖。
dispatch_async(dispatch_get_global_queue(0, 0), ^{

    NSLog(@"=================1");
    
    dispatch_sync(dispatch_get_main_queue(), ^{
        NSLog(@"=================2");
    });
    NSLog(@"=================3");
});

程序會(huì)完成執(zhí)行,為什么不會(huì)出現(xiàn)死鎖。

首先: async 在主線程中 創(chuàng)建了一個(gè)異步線程 加入 全局并發(fā)隊(duì)列,async 不會(huì)等待block 執(zhí)行完成,立即返回,

1,async 立即返回, viewDidLoad 執(zhí)行完畢,及主線程執(zhí)行完畢。
2,同時(shí),全局并發(fā)隊(duì)列立即執(zhí)行異步 block , 打印 1, 當(dāng)執(zhí)行到 sync 它會(huì)等待 block 執(zhí)行完成才返回, 及等待dispatch_get_main_queue() 隊(duì)列中的 mianThread 執(zhí)行完成, 然后才開(kāi)始調(diào)用block 。

因?yàn)? 和 2 幾乎同時(shí)執(zhí)行,因?yàn)? 在全局并發(fā)隊(duì)列上, 2 中執(zhí)行到sync 時(shí) 1 可能已經(jīng)執(zhí)行完成或 等了一會(huì),mainThread 很快退出, 2 等已執(zhí)行后續(xù)內(nèi)容。

dispatch中的其他用法

(1)延時(shí)執(zhí)行
//參數(shù)1:從現(xiàn)在開(kāi)始經(jīng)過(guò)多少納秒,參數(shù)2:調(diào)度任務(wù)的隊(duì)列,參數(shù)3:異步執(zhí)行的任務(wù)
dispatch_after(when, queue, block)
例如:

dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
    // 2秒后異步執(zhí)行這里的代碼...
});

(2)一次性執(zhí)行
應(yīng)用場(chǎng)景:保證某段代碼在程序運(yùn)行過(guò)程中只被執(zhí)行一次,在單例設(shè)計(jì)模式中被廣泛使用。
// 使用dispatch_once函數(shù)能保證某段代碼在程序運(yùn)行過(guò)程中只被執(zhí)行1次

static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
    // 只執(zhí)行1次的代碼(這里面默認(rèn)是線程安全的)
});
最后編輯于
?著作權(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)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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

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