多線程pThread-NSThread-GCD-NSOperation

1、主線程(UI線程)

——顧名思義就是:與UI有關,處理UI事件的線程胸遇。

1.1盆均、主線程的主要作用:

顯示\刷新UI界面、處理UI事件(比如點擊事件,滾動事件,拖拽事件)
使用注意:不能將比較耗時的操作放到主線程中,會有卡的感覺拴曲。耗時操作要放到子線程(后臺線程,非主線程)操作

單條線程是串行凛忿,多條線程同時(來回快速切換)工作是并行

耗時操作放在子線程-后臺線程執(zhí)行

2澈灼、開辟線程:

2.1、線程分類

1店溢、 pthread--POSIX threads 跨平臺(就會與OS有關) 純C語言 但是線程的生命周期由程序員自己管理 幾乎不用
(POSIX表示可移植操作系統(tǒng)接口(Portable Operating System Interface 叁熔,縮寫為 POSIX)

2、 NSThread OC的 可直接操作線程對象 但是線程的生命周期由程序員自己管理 偶爾使用
3逞怨、GCD 替代NSThread等線程技術 充分利用設備的核 C語言 線程的生命周期自動管理 經(jīng)常使用
4者疤、NSOperation 基于GCD 比GCD多了一些更簡單實用的功能 更加面向對象 OC語言 線程的生命周期自動管理 經(jīng)常使用

一、pthread生命周期程序員管理

 // 1叠赦、pthread
 pthread_t pthreadID;
 pthread_create(&pthreadID, NULL, run, NULL);

void *run(void *org){

NSThread *t = [NSThread currentThread];
for (int i = 0; i< 10000; i++) {
        NSLog(@"子線程-- %@",t);
     }
    return NULL;
}

二驹马、NSThread生命周期程序員管理

創(chuàng)建NSThread線程的三種方法:
1 NSThread

1-   NSThread *t = [[NSThread alloc] initWithTarget:self selector:@selector(run) object:nil];
2-   [t start];   

start之后革砸,就執(zhí)行self的run方法,之后的object可以是run中傳入的方法
有優(yōu)先級的設置和獲得糯累、有獲得主線程/當前線程的方法算利,設置/獲得線程的名稱

2 創(chuàng)建線程自動啟動 這個沒有返回值 detach派遣分離

 [NSThread detachNewThreadSelector:@selector(run) toTarget:self withObject:nil];

3 隱式創(chuàng)建子線程(InBackground在后臺執(zhí)行的線程就是子線程)自動啟動線程

  performSelectorInBackground是NSObject的對象方法,誰都可以調(diào)用
   [self performSelectorInBackground:@selector(run) withObject:nil];

pthread與NSThread需要程序員自己管理線程的生命周期泳姐,以下介紹生命周期的常識:

<pre>新建new —(start)— 就緒runnable(在調(diào)度池中) —— 運行running
—— 阻塞blocked(sleep或者等待同步鎖效拭,在內(nèi)存中,不在調(diào)度池中胖秒,就緒的狀態(tài)都不是)
—— 死亡deaded(死亡只是狀態(tài) 可是線程對象還在內(nèi)存中缎患,不在調(diào)度池中)release之后線程對象才會從內(nèi)存中消失。</pre>

只有就緒和運行狀態(tài)時候阎肝,線程才會在調(diào)度池中挤渔。

注意:一旦線程停止(死亡)了,就不能再次開啟任務风题。
2.2判导、線程安全隱患:

資源共享
比如訪問同一塊內(nèi)存,對象 變量 文件讀寫等等 比如銀行的存錢與取錢 還有火車站賣票問題沛硅。

方法一:加互斥鎖

多條線程只能有一條線程去訪問同一塊資源眼刃。
// 任何對象都可以為鎖對象,鎖只能有一把,每次線程想訪問資源的時候摇肌,都會問下鎖對象是否可以訪問資源擂红,所以要用公用的鎖比較好,鎖對象多個的話就不曉得問哪一把鎖了围小,問出的結果就會特別亂篮条,導致錯誤
@synchronized (self) {
self.leftTicketCount -- ;
NSLog(@"賣票之后剩余%d張-%@", self.leftTicketCount, [NSThread currentThread].name);
}

線程的優(yōu)先級問題:
self.thread01.threadPriority = 0.5; // 0.0~1.0 值越大,優(yōu)先級越高吩抓,被調(diào)度的頻率越高
[self.thread01 setThreadPriority:0.5];
[NSThread setThreadPriority:0.5];
[NSThread threadPriority]; // readonly屬性

互斥鎖的優(yōu)缺點---一般加鎖之后性能會比較差:

優(yōu)點:能有效防止因多線程搶奪資源造成的數(shù)據(jù)安全問題
缺點:需要消耗大量CPU資源(其他線程的等待以及每時每刻記錄著鎖的狀態(tài))
注意:只有存在多線程才會加鎖
線程同步:多條線程按順序的執(zhí)行任務--串行執(zhí)行「昂蓿互斥鎖就是運用了線程同步技術

方法二:非原子屬性

原子和非原子屬性:
atomic:OC中屬性默認是原子屬性疹娶,也就是默認在setter方法中加了鎖,性能會比較差伦连,線程安全雨饺,需要消耗大量的資源
nonatomic:非原子屬性,非線程安全惑淳,適合內(nèi)存小的移動設備

(最優(yōu))iOS開發(fā)建議:

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

2.3歧焦、線程間通訊:

線程之間數(shù)據(jù)傳遞 以及 線程之間的切換(比如從子線程跳到主線程)

\- (void) performSelectorOnMainThread:(SEL)aSelector......; // 跳轉到主線程
\- (void) performSelector:(SEL)aSelector onThread:(NSThread *)thr...... // 跳轉到特定的線程上

例子:圖片的下載
/// waitUntilDone YES 等待主線程完成后再子線程繼續(xù)執(zhí)行以后代碼一般設置為NO
/// withObject可以傳入?yún)?shù)
[self performSelectorOnMainThread:<#(nonnull SEL)#> withObject:<#(nullable id)#> waitUntilDone:<#(BOOL)#>]; // “self” 調(diào)用后面的“方法”

三移斩、GCD

兩步驟:

  • 1定制任務
  • 2將任務添加到隊列中

GCD會自動將隊列中的任務取出,放到對應的線程中執(zhí)行(主隊列 對應 主線程,其他隊列 對應 子線程)

  • 兩個方法可以將任務添加到隊列
    (是否具備開啟新線程的能力分了兩種方法):

    dispatch_sync(queue,block) // 同步-----不具備開啟線程的能力
    dispatch_async(queue, block) // 異步——具備開線程的能力
  • 隊列的分類(決定了任務的執(zhí)行方式):

并發(fā)隊列---多個任務并行執(zhí)行向瓷,說明會開多條線程執(zhí)行任務肠套,只能是async函數(shù)。
串行隊列---多個任務一個挨著一個的執(zhí)行

MRC(MannulReference Counting)

并發(fā)隊列:

GCD已經(jīng)提供給我們?nèi)值牟l(fā)隊列猖任,供整個應用使用你稚,不需要手動創(chuàng)建

dispatch_get_global_queue(優(yōu)先級枚舉,0);

優(yōu)先級枚舉值:

#define DISPATCH_QUEUE_PRIORITY_HIGH 2 高的
#define DISPATCH_QUEUE_PRIORITY_DEFAULT 0 默認的 ---- 常用
#define DISPATCH_QUEUE_PRIORITY_LOW (-2) 低的
#define DISPATCH_QUEUE_PRIORITY_BACKGROUND INT16_MIN 最低的

// 異步執(zhí)行--- 有開線程的能力
// 創(chuàng)建隊列(全局隊列默認就是并發(fā)隊列)

dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

// 添加任務到隊列

   dispatch_async(queue, ^{
       NSLog(@"下載圖片1---%@",[NSThread currentThread]);
    });
   dispatch_async(queue, ^{
       NSLog(@"下載圖片2---%@",[NSThread currentThread]);
   });

  #結果:開兩條線程
串行隊列:
  • 1 手動創(chuàng)建 參數(shù):隊列名稱(出現(xiàn)在調(diào)試界面)與傳入?yún)?shù)NULL
    dispatch_queue_t queue = dispatch_queue_create("串行隊列create", NULL);
    // 添加任務到隊列

     dispatch_async(queue, ^{
         NSLog(@"下載圖片1---%@",[NSThread currentThread]);
    });
      dispatch_async(queue, ^{
          NSLog(@"下載圖片2---%@",[NSThread currentThread]);
    });
    
    # 結果:開一條線程
    
  • 2 主隊列 (與主線程相關的隊列)
    dispatch_queue_t queue = dispatch_get_main_queue();

同步執(zhí)行sync:在當前線程執(zhí)行

并行隊列(全局隊列): 不開線程
串行隊列: 不開線程
主隊列(也是串行隊列):放在主線程時候 相互等待卡死狀態(tài)
例外:下載圖片------不開線程朱躺,會把任務放到主隊列的最后刁赖,出現(xiàn)了永遠不能執(zhí)行而是互相等待的狀態(tài)~~~~比如:在B塊代碼在A行之后,把下載圖片sync方式C代碼塊添加到A行上长搀,之后下載圖片的C代碼塊在內(nèi)存的位置是放到了B代碼塊的后面宇弛。由于是在主線程順序執(zhí)行代碼的規(guī)則,在程序代碼先后順序上想執(zhí)行B必須先執(zhí)行完C代碼(B等C)盈滴,在主隊列中先執(zhí)行完B才能執(zhí)行C(C等B)涯肩,所以,互相等待中...

異步執(zhí)行async:開線程

并行隊列(全局隊列): 能開多條線程巢钓,由任務個數(shù)決定
串行隊列: 只開啟一條線程
主隊列(也是串行隊列):block內(nèi)任務可以緩一緩

總結:只要是同步是指病苗,等待對應的任務執(zhí)行完畢后,才繼續(xù)向下執(zhí)行
只要是異步是指症汹,不是串行執(zhí)行硫朦,可以緩一緩再執(zhí)行任務

任務就是放在隊列中的,一個block是一個任務1痴颉Rд埂!B髡丁破婆!

GCD其他函數(shù):

1、延時函數(shù):dispatch_after(dispatch_time(xx,xx), 隊列胸囱, block); // 其中隊列可以是主隊列 也可以是全局隊列!!!!!!!!之前不曉得~
類似:[self performSelectior:@selector(run) withObject:nil afterDelay:2.0];

2祷舀、程序運行過程中,只執(zhí)行一次:
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
// 代碼
});

隊列組:

需求:首先烹笔,分別異步執(zhí)行2個耗時的操作
其次裳扯,等2個異步操作都執(zhí)行完畢后,再回到主線程執(zhí)行操作

===========================代碼==========================

  // 創(chuàng)建隊列組
  dispatch_group_t group = dispatch_group_create();

 // 下載圖片1和2
 dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

  __block UIImage *image01 = nil;
 dispatch_group_async(group, queue, ^{
     // 下載第一張圖片
    NSURL *url = [NSURL URLWithString:@"http://preview.quanjing.com/fod_liv002/fo-11171537.jpg"];
    NSData *imageData = [NSData dataWithContentsOfURL:url];
    NSLog(@"data01-----------%@",imageData);
    image01 = [UIImage imageWithData:imageData];
 });

  __block UIImage *image02 = nil;
 dispatch_group_async(group, queue, ^{
 // 下載第二張圖片
 NSURL *url = [NSURL URLWithString:@"http://scimg.jb51.net/allimg/160716/105-160G61F250436.jpg"];
 NSData *imageData = [NSData dataWithContentsOfURL:url];
 image02 = [UIImage imageWithData:imageData];
});

 // 完成后的通知
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
// 顯示圖片
self.imageView01.image = image01;
self.imageView02.image = image02;

// 合并圖片 開啟圖片上下文

 UIGraphicsBeginImageContextWithOptions(CGSizeMake(200, 100), NO, 0.0);
 [image01 drawInRect:CGRectMake(0, 0, 100, 100)];
 [image02 drawInRect:CGRectMake(100, 0, 100, 100)];
self.imageViewBig.image = UIGraphicsGetImageFromCurrentImageContext();
// 關閉圖片上下文
UIGraphicsEndImageContext();
});

=======================end=======================================

四谤职、NSOperation:

類似于GCD GCD中任務類似NSOperation
隊列類似NSOperationQueue
一個operation對象對應一個任務饰豺。

NSOperatinon是個抽象類

代碼實現(xiàn)1:----沒有創(chuàng)建operation隊列

invocation是“回調(diào)”的意思。

NSInvocationOperation  *invocation = [[NSInvocationOperation alloc] initWithTarget:self  selector:@selector(invocationOperationSomething) object:nil];
[invocation start]; // 沒有添加到隊列的話允蜈,就是主線程

NSBlockOperation *blockOperation = [NSBlockOperation 
blockOperationWithBlock:^{
    NSLog(@"NSBlockOperation---%@", [NSThread currentThread]);
}];
[blockOperation start];  // 沒有添加到隊列的話  就是主線程
  • 默認情況下冤吨,調(diào)用了start方法后蒿柳,并不會開一條新線程去執(zhí)行操作,而是在當前線程(并不一定總是主線程)同步執(zhí)行操作锅很。
    只有將操作NSInvocationOperation放到隊列NSOperationQueue中其馏,才會開啟線程異步執(zhí)行操作。

但是對于NSBlockOperation只要操作數(shù)大于2就會開啟新線程爆安,如果還是1就在當前線程執(zhí)行:

NSBlockOperation *blockOperation = [NSBlockOperation blockOperationWithBlock:^{
   NSLog(@"--blockOperation1 %@--", [NSThread currentThread]);
}];
  // 添加更多操作
 [blockOperation addExecutionBlock:^{
      NSLog(@"--blockExecutionOperation2 %@--", [NSThread currentThread]);
 }];
 [blockOperation start]; //如果任務個數(shù)大于1個叛复,比如n(n>1)個,就會開新線程n-1條扔仓,不會有GCD中的異步同步函數(shù)
  • 需求:不管操作數(shù)是一個還是兩個都要開啟新線程褐奥,那么就要放到隊列中,并且自動執(zhí)行操作翘簇,自動開啟線程撬码。

    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    調(diào)用[queue addOperation:xxx];
    

或者:

  [queue addOperationWithBlock:^{
     NSLog(@"匿名操作--%@--",[NSThread currentThread]);
 }];

生成主隊列 [NSOperationQueue mainQueue];

最大并發(fā)數(shù):

(queue的屬性)同時執(zhí)行的任務數(shù)。2~3為宜版保。如果是2呜笑,線程num = 2,num = 3,num = 4...由整的操作數(shù)決定彻犁。在打印的統(tǒng)一時間上的執(zhí)行操作數(shù)一般都是設置的最大并發(fā)數(shù)叫胁。 maxConcurrentOperationCount屬性。

隊列的取消:

調(diào)用cancelAllOperations取消隊列的所有操作 或者汞幢,操作調(diào)用cancel方法取消單個操作驼鹅。

隊列的暫停:

setSuspended:(BOOL)b; // YES暫停隊列 NO恢復隊列
比如:icon異步下載并且顯示,當tableView上下滾動的時候森篷,顯得有些卡頓(主線程與多個子線程來回
切換的原因)输钩,所以,當滾動的時候仲智,下載操作暫停买乃,tableView停止?jié)L動的時候,下載操作再恢復就可以了钓辆。

操作依賴----面試題

操作的對象方法:[operationA addDependency:operationB];
operationA 依賴于 operationB;
這句代碼要寫在添加隊列之前为牍。否則沒有效果,添加的時候代碼執(zhí)行自動取出操作開始執(zhí)行岩馍。

注意:可以在不同queue之間產(chǎn)生操作依賴,就是不能相互依賴抖韩。

操作監(jiān)聽:operation.completionBlock = ^{
// 寫上操作完成后的所做的事情蛀恩。所在線程是操作的當前線程。也可以寫在操作代碼的后面茂浮,也算是操作監(jiān)聽双谆。操作完成之后在執(zhí)行此段代碼壳咕。
};

例子:自定義operation
在tableViewcell上顯示下載圖片,并且滾動的時候用戶體驗好一些:
1 寫個類繼承自NSOperation
2 定義-(void)main函數(shù)
3 下載操作寫在main函數(shù)內(nèi)顽馋,而且添加自動釋放池谓厘,且需要判斷是否操作被取消,如果被取消了就不再繼續(xù)執(zhí)行以下代碼
4 傳image時候寸谜,使用代理在主線程展示圖片到視圖上

最后保存到沙盒的時候竟稳,使用壓縮圖片
NSData *data = UIImagePNGRepresentation(image);
[data writeToFile:@“” atomically:YES];

最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市熊痴,隨后出現(xiàn)的幾起案子他爸,更是在濱河造成了極大的恐慌,老刑警劉巖果善,帶你破解...
    沈念sama閱讀 206,311評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件诊笤,死亡現(xiàn)場離奇詭異,居然都是意外死亡巾陕,警方通過查閱死者的電腦和手機讨跟,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,339評論 2 382
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來鄙煤,“玉大人晾匠,你說我怎么就攤上這事」堇啵” “怎么了混聊?”我有些...
    開封第一講書人閱讀 152,671評論 0 342
  • 文/不壞的土叔 我叫張陵,是天一觀的道長乾巧。 經(jīng)常有香客問我句喜,道長,這世上最難降的妖魔是什么沟于? 我笑而不...
    開封第一講書人閱讀 55,252評論 1 279
  • 正文 為了忘掉前任咳胃,我火速辦了婚禮,結果婚禮上旷太,老公的妹妹穿的比我還像新娘展懈。我一直安慰自己,他們只是感情好供璧,可當我...
    茶點故事閱讀 64,253評論 5 371
  • 文/花漫 我一把揭開白布存崖。 她就那樣靜靜地躺著,像睡著了一般睡毒。 火紅的嫁衣襯著肌膚如雪来惧。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,031評論 1 285
  • 那天演顾,我揣著相機與錄音供搀,去河邊找鬼隅居。 笑死,一個胖子當著我的面吹牛葛虐,可吹牛的內(nèi)容都是我干的胎源。 我是一名探鬼主播,決...
    沈念sama閱讀 38,340評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼屿脐,長吁一口氣:“原來是場噩夢啊……” “哼涕蚤!你這毒婦竟也來了?” 一聲冷哼從身側響起摄悯,我...
    開封第一講書人閱讀 36,973評論 0 259
  • 序言:老撾萬榮一對情侶失蹤赞季,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后奢驯,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體申钩,經(jīng)...
    沈念sama閱讀 43,466評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,937評論 2 323
  • 正文 我和宋清朗相戀三年瘪阁,在試婚紗的時候發(fā)現(xiàn)自己被綠了撒遣。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,039評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡管跺,死狀恐怖义黎,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情豁跑,我是刑警寧澤廉涕,帶...
    沈念sama閱讀 33,701評論 4 323
  • 正文 年R本政府宣布,位于F島的核電站艇拍,受9級特大地震影響狐蜕,放射性物質發(fā)生泄漏。R本人自食惡果不足惜卸夕,卻給世界環(huán)境...
    茶點故事閱讀 39,254評論 3 307
  • 文/蒙蒙 一层释、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧快集,春花似錦贡羔、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,259評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至院溺,卻和暖如春宵统,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工马澈, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人弄息。 一個月前我還...
    沈念sama閱讀 45,497評論 2 354
  • 正文 我出身青樓痊班,卻偏偏與公主長得像,于是被迫代替她去往敵國和親摹量。 傳聞我的和親對象是個殘疾皇子涤伐,可洞房花燭夜當晚...
    茶點故事閱讀 42,786評論 2 345

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