多線程(一)

基本概念

  1. 進程:系統中正在運行的一個應用程序。
  2. 線程:進程想要執(zhí)行任務图贸,必須要有線程(每1個進程至少要有個1條線程)蹂季,線程是進程的基本單元,一個進程的所有任務都是在線程中執(zhí)行疏日。
  3. 線程的串行:1個線程中任務的執(zhí)行時串行的偿洁,若1個線程中執(zhí)行多個任務,只能一個個的按順序執(zhí)行沟优。同一時間內涕滋,1個線程只能執(zhí)行1個任務。
  4. 多線程
    • 一個進程中開啟多條線程挠阁,每條線程可以并行(同時)執(zhí)行不同的任務
    • 什么叫線程的并行
      • 并行即同時執(zhí)行宾肺。若同時開啟3條線程分別下載3個文件(文件A,文件B侵俗,文件C)
    • 多線程并發(fā)執(zhí)行的原理
      • 同一時間CPU只能處理1條線程锨用。多線程并發(fā)執(zhí)行,只是CPU在多條線程之間進行切換隘谣,如果CPU調度線程的時間足夠快增拥,就造成了多線程并發(fā)執(zhí)行的假象。

5.多線程的優(yōu)缺點

  • 優(yōu)點
    • 能適當提高程序的執(zhí)行效率
    • 能適當提高資源利用率(CPU,內存利用率)
  • 缺點
    • 開啟線程需要占用一定的內存空間寻歧,若開啟大量的線程掌栅,則會占用大量的內存空間,降低程序的性能
    • 線程越多码泛,CPU在調度線程上的開銷就越大
    • 程序設計更加復雜(線程之間的通信猾封,多線程的數據共享)

6.iOS中多線程的實現方案

- pthread
- NSThread
- GCD
- NSOperation

NSThread

  • 創(chuàng)建方式

    • [alloc init];
      • 需要手動開啟線程;
      • 可以拿到線程對象
  • 分離一條子線程;
    - 不可拿到線程對象;

  • 后臺創(chuàng)建一條線程;

    • 不可拿到線程對象;
  • 線程狀態(tài)

  • 新建

  • 就緒

  • 運行

  • 阻塞

  • 死亡(線程死亡之后,不能再重新啟用)

  • 線程安全

    • 多個線程訪問同一塊區(qū)域
    • 增加互斥鎖
    • 相關代碼:@synchoronized(self){}(一般都是使用self)
    • 這叫線程同步
  • 原子屬性和非原子屬性

  • 用@porperty生命一個屬性的時候弟晚,內部會進行三步操作
    1.生成一個有下劃線的成員變量
    2.生成一個setter方法
    3.生成一個getter方法

  • 原子屬性 : atomic(為setter方法加鎖忘衍,默認就是atomic)

  • 非原子屬性:nonatomic(不會為setter方法加鎖)

  • 為什么開發(fā)中經常使用nonatomic而不是atomic

    • atomic 代表線程安全,需要消耗大量資源
    • nonatomic代表非線程安全卿城,適合內存小的移動設備
    • 在開發(fā)中盡量使用nonatomic
    • 盡量避免多線程訪問同一資源
    • 盡量將加鎖、資源搶奪的業(yè)務邏輯交給服務器铅搓,從而減少客戶端的壓力
  • 線程間通信

  • 把程序中的耗時操作盡量放到子線程當中進行

  • 必須要回到主線程中進行UI刷新

GCD

  • 兩個核心概念

    • 任務 : 執(zhí)行什么操作
    • 隊列 : 用來儲存任務的
  • 同步函數/異步函數

  • 同步函數:只能在當前線程中進行任務瑟押,不具備開啟子線程的能力,立刻馬上執(zhí)行任務

  • 異步函數:可以在新線程中執(zhí)行任務星掰,具備開啟子線程的能力

  • 并發(fā)隊列/串行隊列

  • 并發(fā)隊列 : 多個任務可以同時執(zhí)行多望,前提是在異步函數的情況下

  • 可以創(chuàng)建并發(fā)隊列嫩舟,也可以獲取并發(fā)隊列。GCD里本身存在一個并發(fā)隊列

  • 串行隊列 : 任務不能同時執(zhí)行怀偷,需一個接一個的執(zhí)行

  • 主隊列 : 放在主隊列里面的任務只能在主線程中執(zhí)行家厌,并且也是一個接一個的執(zhí)行任務

  • GCD的使用

  • 異步函數 + 并發(fā)隊列 : 開啟子線程,并發(fā)執(zhí)行任務

     //1.創(chuàng)建隊列
    dispatch_queue_t  queue =          dispatch_queue_creat("one",DISPATHC_QUEUE_CONCURRENT);
     //2.封裝任務椎工,將任務添加到隊列中
    dispathc_async(queue,^{
    
    NSLog(@"xxxx");
    
    });
    
  • 異步函數 + 串行隊列 : 開啟一條線程饭于,串行執(zhí)行任務

 //1.創(chuàng)建隊列
 dispatch_queue_t queue = dispatch_queue_creat("two",DISPATCH_QUEUE_SERIAL);

 dispatch_async(queue,^{

  NSLog(@"xxxx");

  });
  • 同步函數 + 并發(fā)隊列 : 不開線程,串行執(zhí)行任務
  //1.創(chuàng)建隊列
  dispatch_queue_t queue = dispatch_queue_creat("three",DISPATCH_QUEUE_CONCURRENT);

  dispatch_sync(queue,^{

  NSLog(@"xxx");

  });
  • 同步函數 + 串行隊列 : 不開線程维蒙,串行執(zhí)行任務
//1.創(chuàng)建隊列
   dispatch_queue_t queue = dispatch_queue_creat("four",DISPATCH_QUEUE_SERIAL);
  dispatch_sync(queue,^{

    NSLog(@"xxx");
  });
  • 異步函數 + 主隊列 : 不開線程掰吕,在主線程中串行執(zhí)行任務
   //1.獲得主隊列
  dispatch_queue_t queue = dispatch_get_main_queue();
  //
  dispatch_async(queue,^{

     NSLog(@"xxx");

  });

  • 同步函數 + 主隊列 : 造成死鎖狀況

    //獲得主隊列
    dispatch_queue_t queue = dispatch_get_main_queue();
    

//
dispatch_sync("six",^{

NSLog(@"xxx");
});


 
- GCD中的線程通信

-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{

dispatch_async(dispatch_get_global_queue(0, 0), ^{
    
    NSURL * url = [NSURL URLWithString:@"http://static.jstv.com/img/2016/5/18/20165181463528366178_0.jpg"];
    
    NSData * imagedate = [NSData dataWithContentsOfURL:url];
    
    UIImage * image = [UIImage imageWithData:imagedate];
    
    
    dispatch_async(dispatch_get_main_queue(), ^{
        
        self.imageView.image = image;
    });
  
    
});

}

 
- GCD的常用函數
 - 延遲

![GCD中的延遲](http://upload-images.jianshu.io/upload_images/1404354-5a589a7e984c4150.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

![調用延遲方法](http://upload-images.jianshu.io/upload_images/1404354-b691033dde114ed0.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

![NSTimer](http://upload-images.jianshu.io/upload_images/1404354-63a320ee97f5eea8.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
 - 一次代碼       

![一次代碼](http://upload-images.jianshu.io/upload_images/1404354-18b974230d99a0ab.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

- 柵欄函數
    
    - 用于異步函數
    - 用于控制多線程執(zhí)行任務順序
    - 在使用柵欄函數的時候,*不能使用全局并發(fā)隊列颅痊,只能進行手動創(chuàng)建*殖熟。
    - 柵欄函數之前的線程執(zhí)行順序,柵欄函數是沒有辦法進行控制的
![柵欄函數](http://upload-images.jianshu.io/upload_images/1404354-7b9aa4054951553b.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

- 快速迭代

 - 開啟多個線程斑响,完成快速迭代操作
 - 類似于for循環(huán)
 - GCD里面的快速迭代是并發(fā)隊列
 - for循環(huán)里面是串行隊列
        小案例:圖片的移動
        思路:(使用了GCD里面的快速迭代)
         1.獲得最初文件夾的路徑
         2.獲得目的文件夾的為路徑
         3.移動文件需要全路徑,需要對最初文件夾下的文件進行路徑拼接
         4.文件名不變菱属,所以目的文件夾的文件路徑也需要進行拼接
         5.然后用文件管理者進行文件移動

- 案例代碼





dispatch_apply(count, dispatch_get_global_queue(0, 0), ^(size_t index) {

  1. 第一個參數:遍歷的次數
  2. 第二個參數: 隊列,必須使用并發(fā)隊列
    3.第三個參數:設置索引

};


![GCD快速迭代 圖片移動](http://upload-images.jianshu.io/upload_images/1404354-b1633fd217ea64d3.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
    
- 隊列組

  - 用來控制隊列任務的完成情況
  
// 創(chuàng)建隊列組
dispatch_group_t group = dispatch_group_create();

// 獲取全局并發(fā)隊列
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);

//將隊列添加到隊列組中舰罚,執(zhí)行任務(下載圖1)
dispatch_group_async(group, queue, ^{
    
    //確定圖片地址
    NSURL * url = [NSURL URLWithString:@"http://img.2258.com/d/file/yule/mingxing/neidi/2016-04-20/6b6d95c044b5282cf5b8c78f73c23c4c.jpg"];
    
    //根據圖片地址下載二進制數據
    NSData * imageData = [NSData dataWithContentsOfURL:url];
    
    //轉換二進制數據
    self.image1 = [UIImage imageWithData:imageData];

});

//將隊列添加到隊列組中照皆,執(zhí)行任務(下載圖2)
dispatch_group_async(group, queue, ^{
    
    //確定圖片地址
    NSURL * url = [NSURL URLWithString:@"http://pic.yesky.com/uploadImages/2016/126/00/7HLRG65LQ5FJ.jpg"];
    
    //根據圖片地址下載二進制數據
    NSData * imageData = [NSData dataWithContentsOfURL:url];
    
    //轉換二進制數據
    self.image2 = [UIImage imageWithData:imageData];
    
});


//當隊列組中的任務完成之后會進入這個方法

dispatch_group_notify(group, dispatch_get_main_queue(), ^{

    //開啟圖片上下文
    UIGraphicsBeginImageContext(CGSizeMake(200, 200));
    
    //繪制圖片到上下文中
    [self.image1 drawInRect:CGRectMake(0, 0, 100, 200)];
    [self.image2 drawInRect:CGRectMake(100, 0, 100, 200)];
    
    //獲得新圖片
    UIImage * newImage = UIGraphicsGetImageFromCurrentImageContext();
    
    //關閉圖片上下文
    UIGraphicsEndImageContext();
    
    //回到主線程,刷新UI
    dispatch_async(dispatch_get_main_queue(), ^{
        
         // 設置圖片
        self.image.image = newImage;
        
    });
    
});

- 這個方法內部并不是阻塞沸停,內部本身是異步的
![方法內部](http://upload-images.jianshu.io/upload_images/1404354-100b4d4670a1fc69.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

- 這個方法是阻塞的膜毁,會等之前的任務執(zhí)行完成之后才能執(zhí)行
![等待](http://upload-images.jianshu.io/upload_images/1404354-c561530c74eb514e.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

- 關于隊列組的另一種寫法

//獲得全局并發(fā)隊列
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);

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

//在該方法后面的異步任務會被納入監(jiān)聽范圍,進入隊列組
dispatch_group_enter(group);

dispatch_async(queue, ^{
    
    NSLog(@"%@",[NSThread currentThread]);
    
    //任務執(zhí)行完成之后離開隊列組
    dispatch_group_leave(group);
});


dispatch_group_enter(group);

dispatch_async(queue, ^{
    
    NSLog(@"%@",[NSThread currentThread]);
    
    dispatch_group_leave(group);
    
});

- 兩個異步函數的方法的區(qū)別

      1. 一個用block塊封裝任務
      2.一個用函數來進行任務的封裝

![兩個方法的區(qū)別](http://upload-images.jianshu.io/upload_images/1404354-dcf9193766c33fbf.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

- 全局并發(fā)隊列和手動創(chuàng)建的并發(fā)隊列的區(qū)別

  - 全局并發(fā)隊列在GCD中本身就存在的愤钾,而手動創(chuàng)建的并發(fā)隊列是重新創(chuàng)建的
  - 在使用柵欄函數的時候瘟滨,必須要使用手動創(chuàng)建的并發(fā)隊列,這樣才能有效果
  - 在iOS6以前能颁,GCD中只要帶有了Creat和retain函數杂瘸,在最后都要進行一次release操作。但是現在GCD已經被納入ARC管理范圍伙菊,已經不需要我們再進行手動release操作了败玉。
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市镜硕,隨后出現的幾起案子运翼,更是在濱河造成了極大的恐慌,老刑警劉巖兴枯,帶你破解...
    沈念sama閱讀 218,682評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件血淌,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機悠夯,發(fā)現死者居然都...
    沈念sama閱讀 93,277評論 3 395
  • 文/潘曉璐 我一進店門癌淮,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人沦补,你說我怎么就攤上這事乳蓄。” “怎么了夕膀?”我有些...
    開封第一講書人閱讀 165,083評論 0 355
  • 文/不壞的土叔 我叫張陵虚倒,是天一觀的道長。 經常有香客問我店诗,道長裹刮,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,763評論 1 295
  • 正文 為了忘掉前任庞瘸,我火速辦了婚禮捧弃,結果婚禮上,老公的妹妹穿的比我還像新娘擦囊。我一直安慰自己违霞,他們只是感情好,可當我...
    茶點故事閱讀 67,785評論 6 392
  • 文/花漫 我一把揭開白布瞬场。 她就那樣靜靜地躺著买鸽,像睡著了一般。 火紅的嫁衣襯著肌膚如雪贯被。 梳的紋絲不亂的頭發(fā)上眼五,一...
    開封第一講書人閱讀 51,624評論 1 305
  • 那天,我揣著相機與錄音彤灶,去河邊找鬼看幼。 笑死,一個胖子當著我的面吹牛幌陕,可吹牛的內容都是我干的诵姜。 我是一名探鬼主播,決...
    沈念sama閱讀 40,358評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼搏熄,長吁一口氣:“原來是場噩夢啊……” “哼棚唆!你這毒婦竟也來了?” 一聲冷哼從身側響起心例,我...
    開封第一講書人閱讀 39,261評論 0 276
  • 序言:老撾萬榮一對情侶失蹤宵凌,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后契邀,有當地人在樹林里發(fā)現了一具尸體摆寄,經...
    沈念sama閱讀 45,722評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,900評論 3 336
  • 正文 我和宋清朗相戀三年坯门,在試婚紗的時候發(fā)現自己被綠了微饥。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,030評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡古戴,死狀恐怖欠橘,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情现恼,我是刑警寧澤肃续,帶...
    沈念sama閱讀 35,737評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站叉袍,受9級特大地震影響始锚,放射性物質發(fā)生泄漏。R本人自食惡果不足惜喳逛,卻給世界環(huán)境...
    茶點故事閱讀 41,360評論 3 330
  • 文/蒙蒙 一瞧捌、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧润文,春花似錦姐呐、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,941評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至骏掀,卻和暖如春鸠澈,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背截驮。 一陣腳步聲響...
    開封第一講書人閱讀 33,057評論 1 270
  • 我被黑心中介騙來泰國打工笑陈, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人侧纯。 一個月前我還...
    沈念sama閱讀 48,237評論 3 371
  • 正文 我出身青樓新锈,卻偏偏與公主長得像,于是被迫代替她去往敵國和親眶熬。 傳聞我的和親對象是個殘疾皇子妹笆,可洞房花燭夜當晚...
    茶點故事閱讀 44,976評論 2 355

推薦閱讀更多精彩內容