簡介:併發
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]; });