//這裡已經開啟了多線程,直接在這裡調用子線程想要調用的代碼 void * run(void *pramga) { NSLog(@"-------"); return NULL; } - (IBAction)btnClick:(id)sender { pthread_t pthread; pthread_create(&pthread, NULL, run, NULL); }
NSThread 面向對象,簡單實用 創建
//隱式創建並啟動線程 [NSThread detachNewThreadSelector:@selector(threadRun:) toTarget:self withObject:@"etund"]; 或者 //創建線程並且啟動線程 [self performSelectorInBackground:@selector(threadRun:) withObject:@"etund"]; 或者 //上述兩個優點是可以快速的創建並啟動線程,方便快捷,但是不能對線程進行多余屬性的設置,而下面一種方法就可以對線程實例屬性的設置,但是要記得要手動開啟線程。 NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(threadRun:) object:@"etund"]; [thread start];
GCD 聽說全稱是叫“牛逼的中樞調度器”,但是我還是喜歡叫大中央調度 純C語言,有很多強大的函數 優勢:(純屬板書) GCD是蘋果公司為多核的並行運算提出的解決方案 GCD會自動利用更多的CPU內核(比如雙核、四核) GCD會自動管理線程的生命周期(創建線程、調度任務、銷毀線程) 程序員只需要告訴GCD想要執行什麼任務,不需要編寫任何線程管理代碼 核心:清楚什麼是任務,隊列 串行與並行不能決定是否要開啟新縣城 並行表明具有創建新線程的能力,但不一定創建新線程 常用GCD內容 串行同步(並行同步/全局隊列同步),由於串行同步,並行同步,以及全局隊列同步都是不創建線程按順序執行,所以歸為一類來講。(不開任何子線程,在主線程中執行)
//串行 dispatch_queue_t queue = dispatch_queue_create("etund", DISPATCH_QUEUE_SERIAL); //並行 //dispatch_queue_t queue = dispatch_queue_create("etund", DISPATCH_QUEUE_CONCURRENT); dispatch_sync(queue, ^{ NSLog(@"%@---1",[NSThread currentThread]); }); dispatch_sync(queue, ^{ NSLog(@"%@----2",[NSThread currentThread]); }); dispatch_sync(queue, ^{ NSLog(@"%@----3",[NSThread currentThread]); }); 運行結果 2015-07-08 19:09:39.611 Pthread-多平台[63090:302902] <NSThread: 0x7fc79142c1d0>{number = 1, name = main}---1 2015-07-08 19:09:39.612 Pthread-多平台[63090:302902] <NSThread: 0x7fc79142c1d0>{number = 1, name = main}----2 2015-07-08 19:09:39.612 Pthread-多平台[63090:302902] <NSThread: 0x7fc79142c1d0>{number = 1, name = main}----3
串行異步 (開啟一個子線程,在子線程中執行)
dispatch_queue_t queue = dispatch_queue_create("etund", DISPATCH_QUEUE_SERIAL); dispatch_async(queue, ^{ NSLog(@"%@---1",[NSThread currentThread]); }); dispatch_async(queue, ^{ NSLog(@"%@---2",[NSThread currentThread]); }); dispatch_async(queue, ^{ NSLog(@"%@---3",[NSThread currentThread]); }); /*運行結果 2015-07-08 19:25:37.811 Pthread-多平台[68041:316565] <NSThread: 0x7fb35850fe30>{number = 3, name = (null)}---1 2015-07-08 19:25:37.811 Pthread-多平台[68041:316565] <NSThread: 0x7fb35850fe30>{number = 3, name = (null)}---2 2015-07-08 19:25:37.811 Pthread-多平台[68041:316565] <NSThread: 0x7fb35850fe30>{number = 3, name = (null)}---3 */
並行異步(全局隊列異步)雖然線程的執行順序不一樣,但是任務從隊列裡面拿出來放進線程的順序是按照先進先出的這是根本,基礎。(有幾個任務,開啟幾個子線程,在各自的子線程中執行)
dispatch_queue_t queue = dispatch_queue_create("etund", DISPATCH_QUEUE_CONCURRENT); dispatch_async(queue, ^{ NSLog(@"%@---1",[NSThread currentThread]); }); dispatch_async(queue, ^{ NSLog(@"%@---2",[NSThread currentThread]); }); dispatch_async(queue, ^{ NSLog(@"%@---3",[NSThread currentThread]); }); // 雖然線程的執行順序不一樣,但是任務從隊列裡面拿出來放進線程的順序是按照先進先出 /* *2015-07-08 19:27:46.060 Pthread-多平台[68694:318231] <NSThread: 0x7f9a89c126b0>{number = 4, name = (null)}---2 2015-07-08 19:27:46.060 Pthread-多平台[68694:318230] <NSThread: 0x7f9a8c211400>{number = 5, name = (null)}---3 2015-07-08 19:27:46.060 Pthread-多平台[68694:318171] <NSThread: 0x7f9a89f9efe0>{number = 3, name = (null)}---1 */
主隊列異步,不開任何子線程,在主線程中運行,也對應了那句話,異步隊列只是具有開啟子線程的能力,但是不一定開子線程。
// 主隊列異步(不開線程) dispatch_async(dispatch_get_main_queue(), ^{ NSLog(@"%@---1",[NSThread currentThread]); }); dispatch_async(dispatch_get_main_queue(), ^{ NSLog(@"%@---2",[NSThread currentThread]); }); dispatch_async(dispatch_get_main_queue(), ^{ NSLog(@"%@---3",[NSThread currentThread]); ; }); /* 2015-07-08 19:34:25.440 Pthread-多平台[70710:322816] <NSThread: 0x7fe0d1418200>{number = 1, name = main}---1 2015-07-08 19:34:25.440 Pthread-多平台[70710:322816] <NSThread: 0x7fe0d1418200>{number = 1, name = main}---2 2015-07-08 19:34:25.440 Pthread-多平台[70710:322816] <NSThread: 0x7fe0d1418200>{number = 1, name = main}---3 */
主隊列同步
- (void)gcdTest_2_4{ NSLog(@"=-=-=-"); // 主隊列同步(阻塞) dispatch_sync(dispatch_get_main_queue(), ^{ NSLog(@"%@---1",[NSThread currentThread]); }); dispatch_sync(dispatch_get_main_queue(), ^{ NSLog(@"%@---2",[NSThread currentThread]); }); dispatch_sync(dispatch_get_main_queue(), ^{ NSLog(@"%@---3",[NSThread currentThread]); }); NSLog(@"=-=-=-"); }
------------至此,基本GCD隊列已經被差不多就這樣了,下面來一下有趣的用法 GCD隊列組,有這麼一個需求,你的一個步驟要在其他步驟完成之後才能完成,也就是後續步驟要依賴於前期步驟,這是GCD隊列組以及Barrier隊列(柵欄隊列)不失為一個好辦法,
//GCD隊列組方式 // (線程組)(線程通訊) // dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); dispatch_group_t group = dispatch_group_create(); // 加載圖片1 dispatch_group_async(group, queue, ^{ NSURL *url = [NSURL URLWithString:@"http://f.hiphotos.baidu.com/image/pic/item/d788d43f8794a4c2e882eb8b0df41bd5ac6e39e8.jpg"]; NSData *data = [NSData dataWithContentsOfURL:url]; self.image1 = [UIImage imageWithData:data]; }); // 加載圖片2 dispatch_group_async(group, queue, ^{ NSURL *url = [NSURL URLWithString:@"http://h.hiphotos.baidu.com/image/pic/item/ac6eddc451da81cbd668501c5666d01608243151.jpg"]; NSData *data = [NSData dataWithContentsOfURL:url]; self.image2 = [UIImage imageWithData:data]; }); // 合並 dispatch_group_notify(group, queue, ^{ UIGraphicsBeginImageContextWithOptions(CGSizeMake(100, 100), NO, 0); [self.image1 drawAsPatternInRect:CGRectMake(0, 0, 50, 100)]; [self.image2 drawAsPatternInRect:CGRectMake(50, 0, 50, 100)]; UIImage *image = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); //GCD線程之間的通信 dispatch_async(dispatch_get_main_queue(), ^{ self.myView.image = image; }); }); //柵欄dispatch_barrier方式 dispatch_queue_t queue = dispatch_queue_create("etund", DISPATCH_QUEUE_CONCURRENT);//創建多個線程 dispatch_async(queue, ^{ NSURL *url = [NSURL URLWithString:@"http://f.hiphotos.baidu.com/image/pic/item/d788d43f8794a4c2e882eb8b0df41bd5ac6e39e8.jpg"]; NSData *data = [NSData dataWithContentsOfURL:url]; self.image1 = [UIImage imageWithData:data]; }); dispatch_async(queue, ^{ NSURL *url = [NSURL URLWithString:@"http://h.hiphotos.baidu.com/image/pic/item/ac6eddc451da81cbd668501c5666d01608243151.jpg"]; NSData *data = [NSData dataWithContentsOfURL:url]; self.image2 = [UIImage imageWithData:data]; }); dispatch_barrier_async(queue, ^{ }); dispatch_async(queue, ^{ UIGraphicsBeginImageContextWithOptions(CGSizeMake(100, 100), NO, 0); [self.image1 drawAsPatternInRect:CGRectMake(0, 0, 50, 100)]; [self.image2 drawAsPatternInRect:CGRectMake(50, 0, 50, 100)]; UIImage *image = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); dispatch_async(dispatch_get_main_queue(), ^{ self.myView.image = image; }); });
延遲加載 dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ // 2秒後異步執行這裡的代碼... }); 快速迭代,當我們不需要注重迭代的順序,只需要快速獲得子元素的時候,GCD的快速迭代為你提供了途徑
// (快速迭代) dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); dispatch_apply(self.view.subviews.count, queue, ^(size_t index) { id obj = self.view.subviews[index]; NSLog(@"---%zu---%@",index,obj); }); /* 2015-07-08 22:26:39.851 Pthread-多平台[972:17366] ---0---<_UILayoutGuide: 0x7fd86b644e00; frame = (0 0; 0 20); hidden = YES; layer = <CALayer: 0x7fd86b643ab0>> 2015-07-08 22:26:39.851 Pthread-多平台[972:17444] ---2---<UIImageView: 0x7fd86b645ba0; frame = (100 100; 100 100); clipsToBounds = YES; userInteractionEnabled = NO; layer = <CALayer: 0x7fd86b643290>> 2015-07-08 22:26:39.851 Pthread-多平台[972:17435] ---1---<_UILayoutGuide: 0x7fd86b6457b0; frame = (0 568; 0 0); hidden = YES; layer = <CALayer: 0x7fd86b644040>> */
once一次性代碼(單例模式設計),有時候我們需要用到單例模式做一些操作例如:傳值時,就會用到單例設計模式,設計單例模式的方法很多,其中最重要的是要做到線程安全,而GCD就提供了這麼一個結構體來保證在創建單例過程中的線程安全
ETPerson.h + (instancetype)sharePerson; ETPerson.m @implementation ETPerson static ETPerson *_person; + (instancetype)sharePerson{ static dispatch_once_t onceDispatch; dispatch_once(&onceDispatch, ^{ _person = [[ETPerson alloc] init]; }); return _person; } + (instancetype)allocWithZone:(struct _NSZone *)zone{ static dispatch_once_t onceDispatch; dispatch_once(&onceDispatch, ^{ _person = [super allocWithZone:zone]; }); return _person; } - (id)copyWithZone:(NSZone *)zone{ return _person; } @end 調用 // onece(單例) NSLog(@"%@----%@------%@-------%@------%@-----%@",[[ETPerson alloc] init],[[ETPerson alloc] init],[ETPerson copy],[ETPerson copy],[ETPerson sharePerson],[ETPerson sharePerson]); /*運行結果 2015-07-08 23:26:57.292 Pthread-多平台[1350:37528] <ETPerson: 0x7fc4bb4041c0>----<ETPerson: 0x7fc4bb4041c0>------ETPerson-------ETPerson------<ETPerson: 0x7fc4bb4041c0>-----<ETPerson: 0x7fc4bb4041c0> */
線程通信 線程之間的通信,線程的通信,在一個進程中,線程往往不是鼓勵存在的,多個線程之間需要經常進行通信 線程間通信的體現 在一個線程傳遞數據給另外一個線程 在一個線程中執行完成任務後,轉到另一個線程繼續執行任務 NSThread的線程通信
//這個方法是指在當前線程運行完後調到主線程裡面運行 - (void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)arg waitUntilDone:(BOOL)wait; //這個方法是指在當前線程運行完後調用另外一個線程裡面運行 - (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(id)arg waitUntilDone:(BOOL)wait
GCD的線程通信,GCD的線程通信十分簡單,只是在代碼塊裡面調用代碼塊,直接上代碼吧。
從子線程回到主線程 dispatch_async( dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ // 執行耗時的異步操作... dispatch_async(dispatch_get_main_queue(), ^{ // 回到主線程,執行UI刷新操作 }); });
至此,iOS裡面簡單的多線程GCD以及NSThread以及PThread部分就入門了,明天更新NSOPeration。