iOS多線程的一些總結(jié)和理解

線程

一個線程中任務(wù)的執(zhí)行是串行的
同一時間內(nèi),一個線程只能執(zhí)行一個任務(wù)
線程是進程中的一條執(zhí)行路徑

多線程

一個進程中可以開啟多條線程愚墓,多條線程可以并行(同時)執(zhí)行不同的任務(wù)

多線程原理

同一時間懂讯,CPU只能處理1條線程,只有一條線程在工作(執(zhí)行)
多線程并發(fā)(同時)執(zhí)行,其實是CPU快速在多條線程之間調(diào)度(切換)
多核CPU是真正意義上的多線程

CPU在多個線程之間調(diào)度屿衅,會消耗大量的CPU資源
每條線程被調(diào)度執(zhí)行的頻次會降低(線程執(zhí)行效率會降低)

優(yōu)點:

能適當(dāng)提高程序的執(zhí)行效率

缺點:

創(chuàng)建線程是有開銷的畅卓,iOS下主要成本包括:內(nèi)核數(shù)據(jù)結(jié)構(gòu)(大約1KB)擅腰、棧空間(子線程512KB翁潘、主線程1MB)
也可以使用-setStackSize設(shè)置趁冈,但必須是4K的倍數(shù),而且最小為16K拜马,創(chuàng)建線程大約需要90毫秒的創(chuàng)建時間
開啟大量的線程渗勘,會降低程序的性能
線程越多,CPU在調(diào)度線程上的開銷越大
程序設(shè)計更加復(fù)雜:比如線程之間的通信俩莽、多線程的數(shù)據(jù)共享

多線程在iOS中的應(yīng)用

一個iOS程序運行后旺坠,默認開啟一個線程,稱為主線程或UI線程

主線程的作用:

顯示豹绪、刷新UI界面
處理UI事件(點擊价淌、滾動、拖拽事件等)

多線程使用注意:

別將比較耗時的操作放到主線程中瞒津,耗時操作會卡住主線程蝉衣,嚴(yán)重影響UI流暢度

iOS多線程實現(xiàn)方案

  • pthread C 通用的多線程API,跨平臺巷蚪,程序員手動管理線程生命周期病毡,使用難度大,iOS中幾乎不用
  • NSThread OC 面向?qū)ο笃ò兀唵我子美材ぃ回撠?zé)創(chuàng)建有送,不用管理線程死亡,偶爾使用
  • GCD C 替代NSThread等線程技術(shù)僧家,可以充分利用設(shè)備的多核雀摘,線程生命周期自動管理,經(jīng)常使用
  • NSOperation OC 基于GCD(底層是GCD)八拱,比GCD多了一些簡單實用的功能阵赠,更加面向?qū)ο螅詣庸芾砭€程生命周期肌稻,經(jīng)常使用清蚀。

pthread

需要引入#import <pthread.h>

//創(chuàng)建方法
int pthread_create(pthread_t * __restrict, const pthread_attr_t * __restrict,
        void *(*)(void *), void * __restrict);
//使用
 pthread_t thread;
 pthread_create(&thread, NULL, func, NULL);

void * func(void *param)
{
    for (NSInteger i = 0; i<50000; i++) {
        NSLog(@"----%zd--%@", i, [NSThread currentThread]);
    }
    return NULL;
}

NSThread

創(chuàng)建、啟動線程

  • 方法一
NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(func) object:nil];
[thread start];
//線程啟動時爹谭,就會在線程thread中執(zhí)行self中的func方法
  • 方法二
//創(chuàng)建線程后自動啟動線程
[NSThread detachNewThreadSelector:(SEL) toTarget:(id) withObject:(id)];
  • 方法三
//隱式創(chuàng)建并啟動線程
[self performSelectorInBackground:(SEL) withObject:(id)];

主線程相關(guān)用法:

+ (NSThread *)mainThread; //獲得主線程
+ (BOOL)isMainThread; //是否為主線程
- (BOOL)isMainThread; //是否為主線程

線程阻塞:

+ (void)sleepUntilDate:(NSDate *)date;
+ (void)sleepForTimeInterval:(NSTimeInterval)ti;

線程死亡:

- (void)exit; // 強制退出線程

GCD

會自動利用更多的CPU內(nèi)核枷邪,會自動管理線程的生命周期,不需要寫管理線程的代碼诺凡。定制任務(wù) 將任務(wù)添加到隊列中 GCD會自動將隊列中的任務(wù)取出东揣,放到線程中去執(zhí)行,任務(wù)的取出遵循FIFO原則绑洛。

任務(wù) (是否有開啟新線程的能力)
  • 同步
    在當(dāng)前線程中執(zhí)行
dispatch_sync(dispatch_queue_t queue, ^(void)block)
  • 異步
    可以在新的線程中執(zhí)行,有開新線程的能力(不是一定會開新線程救斑,比如放在主隊列中)
dispatch_async(dispatch_queue_t queue, ^(void)block)
隊列 (影響任務(wù)的執(zhí)行方式)
  • 并發(fā) (允許開啟多個線程)
// 創(chuàng)建并發(fā)隊列
 dispatch_queue_t queue = dispatch_queue_create("queue.com", DISPATCH_QUEUE_CONCURRENT);
  • 串行
// 創(chuàng)建串行隊列
 dispatch_queue_t queue = dispatch_queue_create("queue.com", DISPATCH_QUEUE_SERIAL);
//  DISPATCH_QUEUE_SERIAL 可以傳NULL

獲取全局并發(fā)隊列

// 兩個參數(shù)  優(yōu)先級 保留參數(shù)傳0
  dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

獲取全局串行主隊列(主隊列中的任務(wù)都在主線程中執(zhí)行)

dispatch_queue_t queue = dispatch_get_main_queue();

不可以在主隊列中添加同步任務(wù)童本,因為同步任務(wù)立刻會執(zhí)行真屯,選成死鎖

| | 并發(fā)隊列 | 手動創(chuàng)建的串行隊列 | 主隊列|
| ------------- |:-------------:| -----:|
| 同步sync |沒有開啟新線程 串行執(zhí)行任務(wù)|沒有開啟新線程 串行執(zhí)行任務(wù)|沒有開啟新線程 串行執(zhí)行任務(wù)|
| 異步async |有開啟新線程 并發(fā)執(zhí)行任務(wù)|有開啟新線程 串行執(zhí)行任務(wù)|沒有開啟新線程 串行執(zhí)行任務(wù)|

GCD一些方法示例
/** 并發(fā) 異步 全局并發(fā)隊列 */
- (void)GCDTest1 {
    /**
     @param identifier 優(yōu)先級
     @param flags      保留參數(shù) 默認傳0
     */
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    dispatch_async(queue, ^{
        NSLog(@"-------1-------");
    });
    dispatch_async(queue, ^{
        NSLog(@"-------2-------");
    });
    dispatch_async(queue, ^{
        NSLog(@"-------3-------");
    });
}
/** 并發(fā) 異步 自定義并發(fā)隊列 */
- (void)GCDTest2 {
    dispatch_queue_t queue = dispatch_queue_create("queue.com", DISPATCH_QUEUE_CONCURRENT);
    dispatch_async(queue, ^{
        NSLog(@"-------1-------");
    });
    dispatch_async(queue, ^{
        NSLog(@"-------2-------");
    });
    dispatch_async(queue, ^{
        NSLog(@"-------3-------");
    });
}
/** 并發(fā) 異步 自定義并發(fā)隊列 圍欄函數(shù) */
- (void)GCDTest3 {
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    dispatch_async(queue, ^{
        NSLog(@"-------1-------");
    });
    dispatch_async(queue, ^{
        NSLog(@"-------2-------");
    });
    dispatch_barrier_async(queue, ^{
        NSLog(@"-------圍欄函數(shù)-------");
    });
    dispatch_async(queue, ^{
        NSLog(@"-------3-------");
    });
}
/** 利用一次性函數(shù)寫單列 */
+ (instancetype)GCDTest4 {
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        _singleton = [[Singleton alloc] init];
    });
    return _singleton;
}
/** 延遲執(zhí)行 */
- (void)GCDTest5 {
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        NSLog(@"延時2秒打印");
    });
}
/** GCD group */
- (void)GCDTest6 {
    dispatch_group_t group = dispatch_group_create();
    dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        NSLog(@"-------1-------");
    });
    dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        NSLog(@"-------2-------");
    });
    dispatch_group_notify(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        NSLog(@"-------3-------");
    });
}

多線程的安全隱患

資源共享:
一塊資源可能會被多個線程共享,比如多個線程訪問同一個對象穷娱、對一個變量绑蔫、同一個文件

解決方法:

互斥鎖
@synchronized(鎖對象){ //需要鎖定的代碼 }
不推薦使用,消耗性能
鎖定1份代碼只能用1把鎖泵额,用多把是無效的
線程同步技術(shù)

原子和非原子屬性
atomic 默認 原子屬性 為setter方法加鎖
線程安全配深,需要消耗大量的資源
nonatomic 非原子屬性 不會為setter方法加鎖
非線程安全,適合內(nèi)存小的移動設(shè)備

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

線程之間的通信

在一個進程中,線程往往不是孤立存在的羞秤,多個線程之間需要經(jīng)常進行通信

如:
一個線程傳遞數(shù)據(jù)給另一個線程
在一個線程中執(zhí)行完特定任務(wù)后缸托,轉(zhuǎn)到另一個線程中繼續(xù)執(zhí)行任務(wù)
常用方法:

- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(nullable id)arg waitUntilDone:(BOOL)wait;
- (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(nullable id)arg waitUntilDone:(BOOL)wait;
NSPort 端口對象 在兩個線程之間通信
NSMessagePort
NSMachPort
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市瘾蛋,隨后出現(xiàn)的幾起案子俐镐,更是在濱河造成了極大的恐慌,老刑警劉巖哺哼,帶你破解...
    沈念sama閱讀 222,681評論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件佩抹,死亡現(xiàn)場離奇詭異叼风,居然都是意外死亡,警方通過查閱死者的電腦和手機棍苹,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,205評論 3 399
  • 文/潘曉璐 我一進店門无宿,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人枢里,你說我怎么就攤上這事懈贺。” “怎么了坡垫?”我有些...
    開封第一講書人閱讀 169,421評論 0 362
  • 文/不壞的土叔 我叫張陵梭灿,是天一觀的道長。 經(jīng)常有香客問我冰悠,道長堡妒,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 60,114評論 1 300
  • 正文 為了忘掉前任溉卓,我火速辦了婚禮皮迟,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘桑寨。我一直安慰自己伏尼,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 69,116評論 6 398
  • 文/花漫 我一把揭開白布尉尾。 她就那樣靜靜地躺著爆阶,像睡著了一般。 火紅的嫁衣襯著肌膚如雪沙咏。 梳的紋絲不亂的頭發(fā)上辨图,一...
    開封第一講書人閱讀 52,713評論 1 312
  • 那天,我揣著相機與錄音肢藐,去河邊找鬼故河。 笑死,一個胖子當(dāng)著我的面吹牛吆豹,可吹牛的內(nèi)容都是我干的鱼的。 我是一名探鬼主播,決...
    沈念sama閱讀 41,170評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼痘煤,長吁一口氣:“原來是場噩夢啊……” “哼凑阶!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起速勇,我...
    開封第一講書人閱讀 40,116評論 0 277
  • 序言:老撾萬榮一對情侶失蹤晌砾,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后烦磁,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體养匈,經(jīng)...
    沈念sama閱讀 46,651評論 1 320
  • 正文 獨居荒郊野嶺守林人離奇死亡哼勇,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,714評論 3 342
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了呕乎。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片积担。...
    茶點故事閱讀 40,865評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖猬仁,靈堂內(nèi)的尸體忽然破棺而出帝璧,到底是詐尸還是另有隱情,我是刑警寧澤湿刽,帶...
    沈念sama閱讀 36,527評論 5 351
  • 正文 年R本政府宣布的烁,位于F島的核電站,受9級特大地震影響诈闺,放射性物質(zhì)發(fā)生泄漏渴庆。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 42,211評論 3 336
  • 文/蒙蒙 一雅镊、第九天 我趴在偏房一處隱蔽的房頂上張望襟雷。 院中可真熱鬧,春花似錦仁烹、人聲如沸耸弄。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,699評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽计呈。三九已至,卻和暖如春僚饭,著一層夾襖步出監(jiān)牢的瞬間震叮,已是汗流浹背胧砰。 一陣腳步聲響...
    開封第一講書人閱讀 33,814評論 1 274
  • 我被黑心中介騙來泰國打工鳍鸵, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人尉间。 一個月前我還...
    沈念sama閱讀 49,299評論 3 379
  • 正文 我出身青樓偿乖,卻偏偏與公主長得像,于是被迫代替她去往敵國和親哲嘲。 傳聞我的和親對象是個殘疾皇子贪薪,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,870評論 2 361

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