OC底層原理二十五:內(nèi)存五大區(qū) & 多線程

OC底層原理 學(xué)習(xí)大綱

本節(jié)將介紹內(nèi)存五大區(qū)多線程:

  1. 內(nèi)存五大區(qū)
  2. 多線程
  3. 互斥鎖與自旋鎖
  4. atomic與nonatomic的區(qū)別
  5. 線程與RunLoop

1. 內(nèi)存五大區(qū)

按照地址排列: 棧區(qū) -> 堆區(qū) -> 全局靜態(tài)區(qū) -> 常量區(qū) -> 代碼區(qū)內(nèi)核區(qū)保留部分不再考慮范圍內(nèi))

image.png

補(bǔ)充說(shuō)明:

  1. 內(nèi)存五大區(qū),實(shí)際是指虛擬內(nèi)存,而不是真實(shí)物理內(nèi)存。(詳情可查看?? 本文第3點(diǎn) 虛擬內(nèi)存與物理內(nèi)存
  2. iOS系統(tǒng)中,應(yīng)用虛擬內(nèi)存默認(rèn)分配4G大小,但五大區(qū)占3G,還有1G五大區(qū)之外的內(nèi)核區(qū)

1.1 棧區(qū)

  1. 函數(shù)內(nèi)部定義的局部變量數(shù)組,都存放在棧區(qū); (比如每個(gè)函數(shù)都有的(id self, SEL _cmd)
  2. 棧區(qū)的內(nèi)存空間系統(tǒng)管理。(函數(shù)調(diào)用時(shí)開(kāi)辟空間,函數(shù)調(diào)用結(jié)束時(shí)回收空間)
  3. 棧是從高地址低地址擴(kuò)展,是一塊連續(xù)的內(nèi)存區(qū)域,遵循FILO先進(jìn)后出原則,效率高。
  4. 棧區(qū)一般在運(yùn)行時(shí)進(jìn)行分配

緩沖區(qū)域

棧區(qū)堆區(qū)中間有小塊未使用內(nèi)存區(qū)域。用于給棧區(qū)堆區(qū)之間創(chuàng)建一個(gè)緩沖區(qū)域

  • 溢出:
    到達(dá)緩沖區(qū)數(shù)據(jù)向小緩沖區(qū)復(fù)制的過(guò)程中,由于沒(méi)有注意小緩沖區(qū)的邊界,導(dǎo)致小緩存區(qū)滿了,從而覆蓋了和小緩存區(qū)相鄰內(nèi)存區(qū)域的其他數(shù)據(jù)引起內(nèi)存問(wèn)題
    (就像桶盛水,水多了,自然越界溢出來(lái)了。)

1.2 堆區(qū)

  1. 空間最大,由我們手動(dòng)管理。(ARC自動(dòng)管理)
  2. 堆是從低地址高地址擴(kuò)展。
  3. malloc、calloc、realloc開(kāi)辟: 堆區(qū)開(kāi)辟空間,可以是不連續(xù)內(nèi)存區(qū)域,以鏈表結(jié)構(gòu)存在(增刪快,查找慢)。返回首地址存放在棧區(qū)。
  4. free回收。釋放對(duì)象在堆區(qū)內(nèi)存,并將棧中的地址指針置空

需要注意:

  • 野指針:提前釋放了,查詢時(shí)找不到內(nèi)容
  • 內(nèi)存泄露 :沒(méi)有釋放,一直占用內(nèi)存
  • 過(guò)度釋放:對(duì)已釋放的對(duì)象進(jìn)行release操作。

1.3 全局靜態(tài)區(qū)(.bss)

  1. 存放全局變量靜態(tài)變量
  2. 空間由系統(tǒng)管理。(程序啟動(dòng)時(shí),開(kāi)辟空間;程序結(jié)束時(shí),回收空間程序執(zhí)行期間一直存在
  3. static修飾的變量僅執(zhí)行一次,生命周期整個(gè)程序運(yùn)行期

1.4 常量區(qū)(.data)

  1. 存放常量(整型、字符型,浮點(diǎn),字符串等),整個(gè)程序運(yùn)行期不能被改變。
  2. 空間由系統(tǒng)管理,生命周期整個(gè)程序運(yùn)行期。

1.5 代碼區(qū)(.text)

  1. 存放程序執(zhí)行CPU指令。(編譯期代碼轉(zhuǎn)換為CPU指令)

defineconst區(qū)別:

define: 宏。編譯期不會(huì)進(jìn)行語(yǔ)法識(shí)別沒(méi)有類型。編譯期會(huì)分配內(nèi)存每次使用都會(huì)進(jìn)行宏替換開(kāi)辟內(nèi)存。

const: 常量。編譯期會(huì)進(jìn)行語(yǔ)法識(shí)別,需要指定類型。編譯期會(huì)分配內(nèi)存,僅在第一次使用時(shí),開(kāi)辟內(nèi)存記錄內(nèi)存地址。后續(xù)調(diào)用時(shí)會(huì)開(kāi)辟內(nèi)存,直接返回記錄的內(nèi)存地址效率。內(nèi)存占用更。

可以通過(guò)以下代碼,加深印象:

- (void)test {
    
    NSInteger i = 666;
    NSLog(@"NSInteger i -> 內(nèi)存地址:%p", &i); // 【局部變量】 棧區(qū)

    NSString * name = @"HT";
    NSLog(@"NSString name -> 內(nèi)存地址: %p", name); // 【字符串內(nèi)容】 存放在常量區(qū)
    NSLog(@"NSString name -> 指針地址: %p", &name);// 【局部變量name的指針】 存放在棧區(qū)
    
    NSObject * objc = [NSObject new];
    NSLog(@"NSObject objc -> 內(nèi)存地址: %p", objc);// 【對(duì)象的內(nèi)容】 存放在堆區(qū)
    NSLog(@"NSObject objc -> 指針地址: %p", &objc);//【對(duì)象的指針】 存放在棧區(qū)
}
  • 打印結(jié)果: (0x7開(kāi)頭: 棧區(qū)0x1開(kāi)頭: 常量區(qū)、 0x6開(kāi)頭: 堆區(qū)
    image.png

2. 多線程

官方文檔: ?? 相關(guān)鏈接

1. 線程和進(jìn)程的定義

  • 線程:
  1. 線程進(jìn)程基本執(zhí)行單元一個(gè)進(jìn)程的所有任務(wù)都在線程執(zhí)行
  2. 進(jìn)程想要執(zhí)行任務(wù),必須得有線程,進(jìn)程至少要有一條線程
  3. 程序啟動(dòng)會(huì)默認(rèn)開(kāi)啟一條線程,這條線程被稱為主線程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. 通過(guò)活動(dòng)監(jiān)視器可以查看Mac系統(tǒng)中所開(kāi)啟的線程。
    image.png

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

  • 地址空間:
    同一進(jìn)程的線程共享本進(jìn)程的地址空間,而進(jìn)程之間則是獨(dú)立的地址空間。
  • 資源擁有:
    同一進(jìn)程內(nèi)的線程共享本進(jìn)程內(nèi)的資源(如內(nèi)存、I/O、cpu等),但進(jìn)程之間資源是相互獨(dú)立的。
  1. 進(jìn)程崩潰后,保護(hù)模式下不會(huì)對(duì)其他進(jìn)程產(chǎn)生影響,但一個(gè)線程崩潰會(huì)導(dǎo)致整個(gè)進(jìn)程死掉。所以多進(jìn)程比多線程健壯。
  2. 進(jìn)程切換時(shí),消耗資源大。涉及頻繁切換時(shí),使用線程好過(guò)進(jìn)程。同樣要求同時(shí)進(jìn)行共享某些變量并發(fā)操作時(shí),只能線程不能用進(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. 線程沒(méi)有地址空間,線程包含進(jìn)程地址空間中

3. 多線程的意義

  • 優(yōu)點(diǎn):
  1. 適當(dāng)提高執(zhí)行效率
  2. 適當(dāng)提高資源利用率(CPU、內(nèi)存等)
  3. 線程上的任務(wù)執(zhí)行完后,線程會(huì)自動(dòng)銷毀
  • 缺點(diǎn):
  1. 開(kāi)啟線程需要占用一定的內(nèi)存空間(參照下面 第5點(diǎn) 線程成本 )
  2. 開(kāi)啟大量線程,會(huì)占用大量內(nèi)存空間,降低程序性能
  3. 線程越多CPU在調(diào)度線程上的開(kāi)銷越大
  4. 程序設(shè)計(jì)更加復(fù)雜(如線程間的通訊,多線程的數(shù)據(jù)共享等)

4. 時(shí)間片

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

  • 單核CPU)同一時(shí)間,CPU只能處理1個(gè)線程

  • 多線程同時(shí)執(zhí)行:
    理論上,只要CPU在多個(gè)線程切換足夠快(時(shí)間片足夠小),就可以做出"同時(shí)執(zhí)行"假象。
    (但實(shí)際上,一個(gè)CPU單次只對(duì)一個(gè)線程進(jìn)行調(diào)度。所以多線程同步需要多個(gè)CPU處理器多核)才可以做到。)

  • 如果線程數(shù)非常,CPU在多個(gè)線程之間切換,會(huì)消耗大量CPU資源。
    每個(gè)線程被調(diào)度的次數(shù)會(huì)降低,線程的執(zhí)行效率會(huì)降低

5. 線程成本

image.png
  • 谷歌翻譯:
image.png
  • 內(nèi)核數(shù)據(jù)結(jié)構(gòu): 1KB
  • 堆空間:iOS主線程:1MB,OSX主線程:8MB、其他輔助線程:512KB
  • 創(chuàng)建時(shí)間:平均90微妙

6. 多線程技術(shù)方案

image.png
1. pthread

pthread是一套通用多線程 API,可以在Unix/ Linux / Windows 等系統(tǒng)跨平臺(tái)使用,使用 C 語(yǔ)言編寫(xiě),需要程序員自己管理線程的生命周期,使用難度較大,我們?cè)?iOS 開(kāi)發(fā)中幾乎不使用。

  • 簡(jiǎn)單使用實(shí)例:
#import <pthread.h>

- (void)createThread {
    // 1. 創(chuàng)建線程:定義一個(gè)pthread_t類型變量
    pthread_t thread;
    // 2. 開(kāi)啟線程:執(zhí)行任務(wù)
    //  參數(shù)1: 要開(kāi)的線程變量
    //  參數(shù)2:線程的屬性
    //  參數(shù)3:子線程的執(zhí)行函數(shù)(任務(wù))
    //  參數(shù)4:函數(shù)入?yún)?    pthread_create(&thread, NULL, run, @"入?yún)?);
    // 3. 設(shè)置子線程的狀態(tài)設(shè)置為detached,該線程運(yùn)行結(jié)束后會(huì)自動(dòng)釋放所有資源
    pthread_detach(thread);
}

void * run(void * param) {
    NSLog(@"%@ %@", [NSThread currentThread], param);
    return NULL;
}
  • 打印結(jié)果:
image.png
  • 其他方法:
    pthread_create(): 創(chuàng)建一個(gè)線程
    pthread_exit(): 終止當(dāng)前線程
    pthread_cancel(): 中斷另外一個(gè)線程的運(yùn)行
    pthread_join():阻塞當(dāng)前的線程,直到另外一個(gè)線程運(yùn)行結(jié)束
    pthread_attr_init():初始化線程的屬性
    pthread_attr_setdetachstate():設(shè)置脫離狀態(tài)的屬性(決定這個(gè)線程在終止時(shí)是否可以被結(jié)合)
    pthread_attr_getdetachstate():獲取脫離狀態(tài)的屬性
    pthread_attr_destroy(): 刪除線程的屬性
    pthread_kill(): 向線程發(fā)送一個(gè)信號(hào)
2. NSThread

NSThread是蘋(píng)果官方提供的,使用起來(lái)比pthread更加面向?qū)ο?,?jiǎn)單易用,可直接操作線程對(duì)象,需要自己管理線程生命周期。實(shí)際開(kāi)發(fā)中偶爾使用。

-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    [self createThread1];
//    [self createThread2];
//    [self createThread3];
}

//MARK: - 創(chuàng)建線程
//創(chuàng)建線程 (手動(dòng)啟動(dòng))
-(void)createThread1{
    // 實(shí)例化一個(gè)線程對(duì)象
    NSThread *thread=[[NSThread alloc]initWithTarget:self selector:@selector(run1) object:nil];
    // 線程名稱
    thread.name = @"Thread1Name";
    
    /**
     NSQualityOfServiceUserInteractive = 0x21, 用戶交互         - 最高(21)
     NSQualityOfServiceUserInitiated = 0x19,   用戶馬上執(zhí)行的事件 - 較高(19)
     NSQualityOfServiceUtility = 0x11,         普通任務(wù)         - 普通(11)
     NSQualityOfServiceBackground = 0x09,      后臺(tái)任務(wù)         - 較低 (9)
     NSQualityOfServiceDefault = -1            常規(guī)            - 最低
     */
    
    // 線程優(yōu)先級(jí)
    thread.qualityOfService = NSQualityOfServiceDefault;
    // 線程啟動(dòng)
    [thread start];
}

//創(chuàng)建線程 (自動(dòng)啟動(dòng))
-(void)createThread2{
    //創(chuàng)建線程后自動(dòng)啟動(dòng)線程
    [NSThread detachNewThreadSelector:@selector(run2:) toTarget:self withObject:@"createThread2"];
}

//創(chuàng)建線程 (自動(dòng)啟動(dòng))
-(void)createThread3{
    //隱式創(chuàng)建線程并啟動(dòng)
    [self performSelectorInBackground:@selector(run2:) withObject:@"createThread3"];
}

//MARK: - 耗時(shí)操作
- (void)run1{
    for (int i=0; i<200; i++) {
        NSLog(@"%d----%@",i,[NSThread currentThread]);
    }
}

- (void)run2:(NSString*)param{
    for (int i=0; i<200; i++) {
        NSLog(@"%d----%@---%@",i,[NSThread currentThread],param);
    }
}
  • 創(chuàng)建:
  1. createThread1手動(dòng)啟動(dòng)線程,可以配置線程屬性(名稱、優(yōu)先級(jí));
  2. createThread2自動(dòng)啟動(dòng)線程,快捷便利,不支持配置線程屬性;
  3. createThread3隱式創(chuàng)建線程并啟動(dòng),快捷便利,不支持配置線程屬性;
  • 任務(wù): run1無(wú)參數(shù),run2帶參數(shù)
  • 其他方法:
    + (NSThread *)mainThread: 獲得主線程
    - (BOOL)isMainThread: 判斷是否為主線程(對(duì)象方法)
    + (BOOL)isMainThread: 判斷是否為主線程(類方法)
    NSThread *current = [NSThread currentThread]: 獲得當(dāng)前線程
    - (void)setName:(NSString *)n: 線程的名字——setter方法
    - (NSString *)name: 線程的名字——getter方法
    - (BOOL)isCancelled:判斷是否已取消
    - (BOOL)isFinished:判斷是否已經(jīng)結(jié)束
    - (BOOL)isExecuting:判斷是否正在執(zhí)行

  • 狀態(tài)控制方法:
    - (void)start:線程進(jìn)入就緒狀態(tài) -> 運(yùn)行狀態(tài)。當(dāng)線程任務(wù)執(zhí)行完畢,自動(dòng)進(jìn)入死亡狀態(tài)
    - (void)cancel: 線程取消
    - (void)setName:(NSString *)n: 線程的名字——setter方法
    + (void)sleepUntilDate:(NSDate *)date:阻塞(暫停)線程方法
    + (void)sleepForTimeInterval:(NSTimeInterval)ti:線程進(jìn)入阻塞狀態(tài)
    + (void)exit:線程進(jìn)入死亡狀態(tài)(立即終止除主線程以外所有線程)

  • 線程之間的通信:
    在主線程上執(zhí)行操作
    - (void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)arg waitUntilDone:(BOOL)wait
    - (void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)arg waitUntilDone:(BOOL)wait modes:(NSArray<NSString *> *)array
    在指定線程上執(zhí)行操作
    - (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(id)arg waitUntilDone:(BOOL)wait modes:(NSArray *)array NS_AVAILABLE(10_5, 2_0);
    - (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(id)arg waitUntilDone:(BOOL)wait NS_AVAILABLE(10_5, 2_0);
    在當(dāng)前線程上執(zhí)行操作,調(diào)用 NSObject 的 performSelector:相關(guān)方法
    - (id)performSelector:(SEL)aSelector;
    - (id)performSelector:(SEL)aSelector withObject:(id)object;
    - (id)performSelector:(SEL)aSelector withObject:(id)object1 withObject:(id)object2;

  • 售票案例(線程安全,多讀單寫(xiě)
@interface ViewController ()
@property (nonatomic, assign) NSInteger ticketSurplusCount; // 剩余票數(shù)
@property (nonatomic, strong) NSThread *ticketSaleWindow1; // 線程1 
@property (nonatomic, strong) NSThread *ticketSaleWindow2; // 線程2
@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];

    [self initTicketStatusSave];
}

/**
 * 初始化火車票數(shù)量、賣票窗口(線程安全)、并開(kāi)始賣票
 */
- (void)initTicketStatusSave {
    // 1. 設(shè)置剩余火車票為 50
    self.ticketSurplusCount = 50;
    
    // 2. 設(shè)置北京火車票售賣窗口的線程
    self.ticketSaleWindow1 = [[NSThread alloc]initWithTarget:self selector:@selector(saleTicketSafe) object:nil];
    self.ticketSaleWindow1.name = @"北京火車票售票窗口";
    
    // 3. 設(shè)置上?;疖嚻笔圪u窗口的線程
    self.ticketSaleWindow2 = [[NSThread alloc]initWithTarget:self selector:@selector(saleTicketSafe) object:nil];
    self.ticketSaleWindow2.name = @"上?;疖嚻笔燮贝翱?;
    
    // 4. 開(kāi)始售賣火車票
    [self.ticketSaleWindow1 start];
    [self.ticketSaleWindow2 start];
}

/**
 * 售賣火車票(線程安全)
 */
- (void)saleTicketSafe {
    while (1) {
        // 互斥鎖
        @synchronized (self) {
            //如果還有票,繼續(xù)售賣
            if (self.ticketSurplusCount > 0) {
                self.ticketSurplusCount --;
                NSLog(@"%@", [NSString stringWithFormat:@"剩余票數(shù):%ld 窗口:%@", self.ticketSurplusCount, [NSThread currentThread].name]);
                [NSThread sleepForTimeInterval:0.2];
            }
            //如果已賣完,關(guān)閉售票窗口
            else {
                NSLog(@"所有火車票均已售完");
                break;
            }
        }
    }
}
3. GCD
  • GCD(Grand Central Dispatch),大中央調(diào)度。
    對(duì)線程操作進(jìn)行了封裝,加入了很多新的特性,內(nèi)部進(jìn)行了效率優(yōu)化,提供了簡(jiǎn)潔的C語(yǔ)言接口,使用簡(jiǎn)單高效,是蘋(píng)果推薦的方式。使用頻率高
#pragma mark - GCD演練
/**
 并發(fā)隊(duì)列,同步執(zhí)行
 */
- (void)gcdDemo4 {
    // 1. 隊(duì)列
    dispatch_queue_t queue = dispatch_queue_create("itcast", DISPATCH_QUEUE_CONCURRENT);

    // 2. 同步執(zhí)行任務(wù)
    for (int i = 0; i < 10; i++) {
        dispatch_sync(queue, ^{
            NSLog(@"%@ %d", [NSThread currentThread], i);
        });
    }
}

/**
 并發(fā)隊(duì)列,異步執(zhí)行
 */
- (void)gcdDemo3 {
    // 1. 隊(duì)列
    dispatch_queue_t queue = dispatch_queue_create("itcast", DISPATCH_QUEUE_CONCURRENT);

    // 2. 異步執(zhí)行任務(wù)
    for (int i = 0; i < 10; i++) {
        dispatch_async(queue, ^{
            NSLog(@"%@ %d", [NSThread currentThread], i);
        });
    }

}

/**
 串行隊(duì)列,異步執(zhí)行
 */
- (void)gcdDemo2 {
    // 1. 隊(duì)列
    dispatch_queue_t queue = dispatch_queue_create("itcast", NULL);

    // 2. 異步執(zhí)行任務(wù)
    for (int i = 0; i < 10; i++) {
        dispatch_async(queue, ^{
            NSLog(@"%@ %d", [NSThread currentThread], i);
        });
    }
}

/**
 串行隊(duì)列,同步執(zhí)行(開(kāi)發(fā)中非常少用)
 */
- (void)gcdDemo1 {

    // 1. 隊(duì)列
//    dispatch_queue_t queue = dispatch_queue_create("icast", DISPATCH_QUEUE_SERIAL);
    dispatch_queue_t queue = dispatch_queue_create("icast", NULL);
    NSLog(@"執(zhí)行前----");

    // 執(zhí)行任務(wù)
    for (int i = 0; i < 10; i++) {
        NSLog(@"調(diào)度----");

        // 在隊(duì)列中"同步"執(zhí)行任務(wù),串行對(duì)列添加同步執(zhí)行任務(wù),會(huì)立即被執(zhí)行
        dispatch_sync(queue, ^{
            NSLog(@"%@ %d", [NSThread currentThread], i);
        });
    }
    NSLog(@"for 后面");
}
4. NSOperation

NSOperation是基于GCD的一個(gè)抽象基類,將線程封裝成要執(zhí)行的操作,不需要管理線程的生命周期同步,但比GCD可控性強(qiáng)。例如可以加入操作依賴addDependency)、設(shè)置操作隊(duì)列最大可并發(fā)執(zhí)行的操作個(gè)數(shù)setMaxConcurrentOperationCount)、取消操作(cancel)等。

NSOperation作為抽象基類不具備封裝我們的操作的功能,需要使用兩個(gè)它的實(shí)體子類:NSBlockOperationNSInvocationOperation,或者繼承NSOperation自定義子類。

NSBlockOperationNSInvocationOperation用法的主要區(qū)別是:前者執(zhí)行指定的方法,后者執(zhí)行代碼塊,相對(duì)來(lái)說(shuō)后者更加靈活易用。NSOperation操作配置完成后便可調(diào)用start函數(shù)在當(dāng)前線程執(zhí)行,如果要異步執(zhí)行避免阻塞當(dāng)前線程則可以加入NSOperationQueue異步執(zhí)行。

  • 測(cè)試代碼
- (void)opDemo2 {
    NSOperationQueue *q = [[NSOperationQueue alloc] init];
    [q addOperationWithBlock:^{
        NSLog(@"耗時(shí)操作 %@", [NSThread currentThread]);

        [[NSOperationQueue mainQueue] addOperationWithBlock:^{
            NSLog(@"更新UI %@", [NSThread currentThread]);
        }];
    }];
}

- (void)opDemo1 {
    NSInvocationOperation *op = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(downloadImage:) object:@"Invocation"];

    // start 會(huì)立即在當(dāng)前線程執(zhí)行 selector 方法
    //    [op start];

    // 將操作添加到隊(duì)列,會(huì)自動(dòng)異步執(zhí)行
    NSOperationQueue *q = [[NSOperationQueue alloc] init];
    [q addOperation:op];
}

- (void)downloadImage:(id)obj {
    NSLog(@"%@ %@",  [NSThread currentThread], obj);
}

7. 線程生命周期

image.png
    1. 新建: new新建線程后,調(diào)用start后,并不會(huì)立即執(zhí)行,而是進(jìn)入就緒狀態(tài),等待CPU的調(diào)度。
    1. 運(yùn)行: CPU調(diào)度當(dāng)前線程,進(jìn)入運(yùn)行狀態(tài),開(kāi)始執(zhí)行任務(wù)。
      如果當(dāng)前線程還在運(yùn)行中,CPU從可調(diào)度池中調(diào)用其他線程,來(lái)執(zhí)行此任務(wù)。
    1. 阻塞: 運(yùn)行中的任務(wù),被調(diào)用sleep/等待同步鎖時(shí),會(huì)進(jìn)入阻塞狀態(tài)。所有線程都停止,等待sleep結(jié)束/獲取同步鎖,才會(huì)回到就緒狀態(tài)
    1. 死亡: 運(yùn)行中的任務(wù),在任務(wù)執(zhí)行完被強(qiáng)制退出時(shí),線程自動(dòng)進(jìn)入Dead銷毀。

線程池調(diào)度:

image.png

飽和策略:

image.png

3. 互斥鎖與自旋鎖

  • 互斥鎖:
  1. 保證鎖內(nèi)代碼,同一時(shí)間,只有一條線程能夠執(zhí)行
  2. 互斥鎖的鎖定范圍,應(yīng)該盡量小,鎖定范圍越大,效率越差
  • 互斥鎖參數(shù):
  1. 能夠加鎖的任意NSObject對(duì)象
  2. 鎖對(duì)象要保證所有線程都能夠訪問(wèn)
  3. 如果代碼只有一個(gè)地方需要加鎖,大多都使用self,這樣可以避免單獨(dú)再創(chuàng)建一個(gè)鎖對(duì)象
  • 自旋鎖
    耗性能循環(huán)輪循是否可執(zhí)行。自旋鎖內(nèi)容應(yīng)盡可能,保障盡快完成鎖內(nèi)任務(wù)。

互斥鎖與自旋鎖的區(qū)別:

互斥鎖是被動(dòng)等待代碼觸發(fā),再上鎖。自旋鎖是主動(dòng)輪循請(qǐng)求資源。所以自旋鎖更消耗資源。

  • 要求立即執(zhí)行,任務(wù)資源較小(執(zhí)行耗時(shí)短)時(shí),可選擇自旋鎖。
  • 被動(dòng)觸發(fā),任務(wù)資源較大(執(zhí)行耗時(shí)長(zhǎng))時(shí),選擇互斥鎖。

4. atomic與nonatomic的區(qū)別

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

  • atomic 原子屬性。線程安全,需要消耗大量的資源。是默認(rèn)值。
    atomic是針對(duì)多線程設(shè)計(jì)的,本身有自旋鎖, 實(shí)現(xiàn)單寫(xiě)多讀:?jiǎn)蝹€(gè)線程寫(xiě)入,多個(gè)線程可以讀取。

iOS官方建議:
所有屬性都聲明為nonatomic,避免多線程搶奪同一塊資源。
盡量將加鎖、資源搶奪的業(yè)務(wù)邏輯交給服務(wù)器端處理減小移動(dòng)客戶端的壓力**

5. 線程與RunLoop

  1. RunLoop線程一一對(duì)應(yīng)的,一個(gè)runloop對(duì)應(yīng)一個(gè)核心的線程。

為什么說(shuō)是核心的,是因?yàn)?code>runloop是可以嵌套的,但是核心的只能有一個(gè),他們的關(guān)系保存在一個(gè)全局字典里。

  1. Runloop是來(lái)管理線程的,當(dāng)線程的runloop被開(kāi)啟后,線程會(huì)在執(zhí)行完任務(wù)后進(jìn)入休眠狀態(tài)有任務(wù)就會(huì)被喚醒去執(zhí)行任務(wù)。

  2. Runloop第一次獲取時(shí)被創(chuàng)建,在線程結(jié)束時(shí)被銷毀。

  3. 對(duì)于主線程來(lái)說(shuō),runloop在程序一啟動(dòng)就默認(rèn)創(chuàng)建好了。

  4. 對(duì)于子線程來(lái)說(shuō),runloop懶加載的,只有當(dāng)我們使用時(shí)才會(huì)創(chuàng)建,所以在子線程用定時(shí)器要注意:確保子線程的runloop創(chuàng)建,不然定時(shí)器不會(huì)回調(diào)

最后編輯于
?著作權(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ù)。

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