GCD詳解

簡介:併發

GCD(全稱Grand CentralDispatch) 
純C語言

GCD優點:app

1.爲多核運算提供
2.自動管理線程的生命週期(建立線程、調度任務、銷燬線程、),相比NSTread須要手動管理線程和生命週期更方便
3.只須要告訴GCD想要執行什麼任務,不須要編寫任何線程管理代碼

GCD中有兩個核心概念: 任務和隊列異步

1.任務: 執行什麼操做
2.隊列: 用來存聽任務 (串行隊列、併發隊列)
3.將任務添加到隊列,GCD會自動將隊列中任務取出,放到對應線程中執行,任務取出遵循:先進先出,後進後出

 

提早說一下參數類型:async

第一個參數: 隊列的名稱
第二個參數: 告訴系統須要建立一個併發隊列仍是串行隊列
DISPATCH_QUEUE_SERIAL :串行
DISPATCH_QUEUE_CONCURRENT 併發
第一個參數: iOS8之前是優先級, iOS8之後是服務質量
iOS8之前
*  - DISPATCH_QUEUE_PRIORITY_HIGH          高優先級 2
*  - DISPATCH_QUEUE_PRIORITY_DEFAULT:      默認的優先級 0
*  - DISPATCH_QUEUE_PRIORITY_LOW:          低優先級 -2
*  - DISPATCH_QUEUE_PRIORITY_BACKGROUND:

iOS8之後
*  - QOS_CLASS_USER_INTERACTIVE  0x21 用戶交互(用戶迫切想執行任務)
*  - QOS_CLASS_USER_INITIATED    0x19 用戶須要
*  - QOS_CLASS_DEFAULT           0x15 默認
*  - QOS_CLASS_UTILITY           0x11 工具(低優先級, 蘋果推薦將耗時操做放到這種類型的隊列中)
*  - QOS_CLASS_BACKGROUND        0x09 後臺
*  - QOS_CLASS_UNSPECIFIED       0x00 沒有設置

第二個參數: 廢物

  

執行任務: 同步、異步、柵欄函數

同步 dispatch_sync : 只能在當前線程中執行任務,不具有開啓新線程的能力工具

dispatch_sync(<#dispatch_queue_t queue#>, <#^(void)block#>)

異步 dispatch_async:能夠在新的線程中執行任務,具有開啓新線程的能力spa

dispatch_async(<#dispatch_queue_t queue#>, <#^(void)block#>)

另一個執行任務的方法:dispatch_barrier_async 柵欄線程

dispatch_barrier_async(<#dispatch_queue_t queue#>, <#^(void)block#>)

(場景:在前面的任務執行結束後它才執行,並且它後面的任務等它執行完成以後纔會執行 注意:這個queue不能是全局的併發隊列)code

 

隊列:串行與並行orm

使用dispatch_queue_create函數建立隊列

dispatch_queue_t dispatch_queue_create(
    const char *Label,//隊列名稱
    dispatch_queue_attr_t attr//隊列的類型
);

 

併發隊列: 多任務同時執行,開啓多個線程

1.手動建立併發隊列dispatch_queue_create

dispatch_queue_t queue = dispatch_queue_create(@"隊列名稱", DISPATCH_QUEUE_CONCURRENT);

2.GCD默認提供全局併發隊列dispatch_get_global_queue

dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

串行隊列:任務一個個的執行

1.手動建立串行隊列dispatch_queue_create

dispatch_queue_t queue = dispatch_queue_create(@"隊列名稱", NULL);

2.系統自帶一種特殊串行隊列

dispatch_queue_t queue = dispatch_get_main_queue();

 

同步、異步區別

同步: 在當前線程中執行任務,不開闢新的線程
異步:能夠在新的線程中執行任務,能夠開闢新線程

併發、串行區別

併發: 容許多個任務併發執行
串行: 一個任務執行完以後再執行下一個任務

同步函數 + 主隊列(在子隊列中進行)

dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
dispatch_async(queue, ^{
    //block會在子線程中執行
    dispatch_queue_t queue = dispatch_get_main_queue();
    dispatch_sync(queue, ^{
        //block必定會在主線程執行
    });
});

異步 + 主隊列: 不會建立新的線程,而且任務都是在主線程中執行

dispatch_queue_t queue = dispatch_get_main_queue();
   dispatch_async(queue, ^{
    //只要任務添加到主隊列中,那麼任務就必定會在主線程中執行
});

同步 + 併發 : 不會建立新的線程

//建立一個併發隊列
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
//將任務添加到隊列中
dispatch_sync(queue, ^{
    NSLog(@"任務1");
});
dispatch_sync(queue, ^{
    NSLog(@"任務2");
});
dispatch_sync(queue, ^{
    NSLog(@"任務3");
});
NSLog(@"任務");
輸出: 任務1 任務2 任務3 任務

 同步 + 串行 : 不會建立新的線程(若是調用同步函數,會等同步函數中得任務執行完以後再執行後面的的代碼)

//建立一個串行隊列
dispatch_queue_t queue = dispatch_queue_create("aaa", NULL);
//將任務添加到隊列中
dispatch_sync(queue, ^{
    NSLog(@"任務1");
});
dispatch_sync(queue, ^{
    NSLog(@"任務2");
});
dispatch_sync(queue, ^{
    NSLog(@"任務3");
});
NSLog(@"任務");

異步 + 串行 : 會開啓新的線程(只有一個) 若是調用一步函數,那麼不用等函數中得任務執行完畢就能夠執行後面的代碼

//建立串行隊列
dispatch_queue_t queue = dispatch_queue_create("aa", DISPATCH_QUEUE_SERIAL);
dispatch_async(queue, ^{
    NSLog(@"任務1");
});
dispatch_async(queue, ^{
    NSLog(@"任務2");
});
dispatch_async(queue, ^{
    NSLog(@"任務3");
});
NSLog(@"任務");

異步 + 併發 : 會開啓新的線程(若是任務多了會開啓多個線程)

dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
dispatch_async(queue, ^{
    NSLog(@"任務1");
});
dispatch_async(queue, ^{
    NSLog(@"任務2");
});
dispatch_async(queue, ^{
    NSLog(@"任務3");
});
NSLog(@"任務");

 

線程間的通訊

子線程回到主線程

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    //執行耗時的異步操做
    NSLog(@"1");
});
dispatch_async(dispatch_get_main_queue(), ^{
   //這裏使用的是dispatch_async:不用等block執行完,就會調用下面的打印代碼,若是替換爲使用dispatch_sync同步函數:那麼會等block執行更新UI完畢,纔會執行最後一句打印代碼
    NSLog(@"2");
});
NSLog(@"刷新UI界面結束");

實例:  子線程下載圖片,主線程刷新UI

//除主隊列之外,隨便寫一個隊列
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
//調用異步函數
dispatch_async(queue, ^{
    //耗費時間
    UIImage * image = [UIImage imageWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:@"http://images.cnblogs.com/cnblogs_com/tupx/465992/o_torus_trianglestrip1.JPG"]]];
    dispatch_sync(dispatch_get_main_queue(), ^{
        //刷新主界面
        self.imageView.image = image;
    });
});

 

延時執行

 iOS經常使用延時執行,調用NSObject方法 (performSelector一旦執行號延時任務,不會卡住當前線程)

[self performSelector:@selector(run) withObject:nil afterDelay:1.0];

使用GCD函數

dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3.0 * NSEC_PER_SEC)), dispatch_get_global_queue(0, 0), ^{
        NSLog(@"3秒後執行");
    });

NSTimer

[NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(startTest) userInfo:nil repeats:NO];
- (void)startTest{
     NSLog(@"---- begin-----");
}

[NSThread sleepForTimeInterval:3]; 延時執行不要使用sleepForTimeInterval (會卡住線程)

[NSThread sleepForTimeInterval:3];卡住線程3秒

 

一次性代碼

dispatch_once能夠保證函數內代碼在程序運行期間值執行一次(不能當作懶加載使用)

static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
    NSLog(@"一次性的代碼");
});

 

快速迭代

第一個參數: 須要遍歷幾回
第二個參數: 決定第三個參數的block在哪一個線程中執行
第三個參數: 回掉
dispatch_apply(10, 隊列queue, ^(size_tindex){
  //執行10次代碼,index順序不肯定
});
//定義變量記錄原始文件夾和目標文件夾的路徑
NSString * sourcePatch = @"/原始文件";
NSString * destPath = @"/目標文件";

//取出原始文件中全部的文件
NSFileManager * manager = [NSFileManager defaultManager];
NSArray * files = [manager subpathsAtPath:sourcePatch];

dispatch_apply(files.count, dispatch_get_global_queue(0, 0), ^(size_t index) {
    NSString *fileName = files[index];
    //生成原始文件的路徑
    NSString *sourceFilePath = [sourcePatch stringByAppendingPathComponent:fileName];
    //生成目標文件的路徑
    NSString *destFilePath = [destPath stringByAppendingPathComponent:fileName];
    //利用NSFileManager拷貝文件
    [manager moveItemAtPath:sourceFilePath toPath:destFilePath error:nil];
});
相關文章
相關標籤/搜索