iOS 多線程之GCD

什麼是GCD?

  • 全稱是 Grand Central Dispatch
  • 純 C 語言,提供了很是多強大的函數

GCD的優點

  • GCD 是蘋果公司爲多核的並行運算提出的解決方案
  • GCD 會自動利用更多的CPU內核(好比雙核、四核)
  • GCD 會自動管理線程的生命週期(建立線程、調度任務、銷燬線程)
  • 程序員只須要告訴 GCD 想要執行什麼任務,不須要編寫任何線程管理代碼

GCD作了啥


將任務添加到隊列,而且指定執行任務的函數程序員

//1.建立任務block
    dispatch_block_t task = ^{
        NSLog(@"要執行的任務");
    };
    //2.獲取/建立隊列
    dispatch_queue_t getQueue = dispatch_get_global_queue(0, 0);
    //3.調用同/異步函數
    dispatch_async(getQueue, task);
複製代碼

隊列與函數

隊列

image.png

  • 串行隊列:任務必須按照前後順序執行,先進先出。
  • 併發隊列:任務可同時執行。

系統自動建立的隊列安全

  • 主隊列:dispatch_get_main_queue();
    • 是一條捆綁在主線程上的隊列
    • 專門用來在主線程上調度任務的隊列
    • 不會開啓線程
    • 若是當前主線程正在有任務執行,那麼不管主隊列中當前被添加了什麼任務,都不會被調度
    • 主隊列用於在應用程序中與主線程和主runloop交互。
    • 主隊列不徹底像普通串行隊列。用於非UI應用程序的進程時,它可能會產生沒必要要的反作用。
  • 全局併發隊列:dispatch_get_global_queue(0, 0)
    • 全局隊列是一個併發隊列
    • 在使用多線程開發時,若是對隊列沒有特殊需求,在執行異步任務時,能夠直接使用全局隊列
函數
  • 同步函數:dispatch_sync(dispatch_queue_t queue, dispatch_block_t block);
  • 異步函數:dispatch_async(dispatch_queue_t queue, dispatch_block_t block);
隊列與函數相結合
  • 同步串行隊列
    • 不會開啓線程,在當前線程執行任務
    • 任務串行執行,任務一個接着一個
    • 會產生堵塞
  • 同步併發隊列
    • 不會開啓線程,在當前線程執行任務
    • 任務一個接着一個
  • 異步串行隊列
    • 開啓線程一條新線程
    • 任務一個接着一個
  • 異步併發隊列
    • 開啓線程,在當前線程執行任務
    • 任務異步執行,沒有順序,CPU調度有關

若是在同步函數或者串行隊列中再加入一個同步函數或者串行隊列,就很容易形成死鎖。bash

死鎖
  • 主線程由於你同步函數的緣由等着先執行任務
  • 主隊列等着主線程的任務執行完畢再執行本身的任務
  • 主隊列和主線程相互等待會形成死鎖

tips: 1.主隊列相似於串行隊列。 2.主線程執行任務,也是按照代碼從上至下執行,因此主線程中也相似執行着同步函數。多線程

GCD的應用

單例
@interface Person : NSObject
+(instancetype)shareInstance;
@end


static Person *person = nil;
@implementation Person
+(instancetype)shareInstance{
    static dispatch_once_t token;
    if (person == nil) {
        dispatch_once(&token, ^{
            person  = [[Person alloc]init];
        });
    }
    return person;
}
+(instancetype)allocWithZone:(struct _NSZone *)zone{
    return  [self shareInstance];
}
- (id)copyWithZone:(NSZone *)zone
{
    return self;
}

- (id)mutableCopyWithZone:(NSZone *)zone
{
    return self;
}

@end

複製代碼
延遲執行
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        NSLog(@"延遲執行");
});
複製代碼
信號量控制併發數(也能夠起到鎖的做用)
//創造信號量,設置併發數爲2
dispatch_semaphore_t lock =  dispatch_semaphore_create(2);
//信號量等待
dispatch_semaphore_wait(lock, DISPATCH_TIME_FOREVER);
//信號量釋放。
dispatch_semaphore_signal(lock);
複製代碼
柵欄函數

控制任務執行順序
dispatch_barrier_async:前面的任務執行完畢纔會來到這裏
dispatch_barrier_sync:做用相同,可是這個會堵塞線程,影響後面的任務執行(不論任務是否在隊列內)。
重點: 柵欄函數只能控制同一併發隊列,且這條隊列須要本身建立,不一樣隊列將沒法生效。併發

// 建立併發隊列
    dispatch_queue_t concurrentQueue = dispatch_queue_create("myqueue", DISPATCH_QUEUE_CONCURRENT);
    //異步函數一   
    dispatch_async(concurrentQueue, ^{
        NSLog(@"任務一完成");
    }); 
    //異步函數二   
    dispatch_async(concurrentQueue, ^{
        NSLog(@"任務二完成");
    });
    //柵欄函數
    dispatch_barrier_async(concurrentQueue, ^{
        NSLog(@"上面兩個任務都完成了,總算輪到我了。");
    });
    NSLog(@"若是柵欄函數是異步的,我會先執行;但若是是同步的話,我就得在柵欄函數執行完後,才能執行");
複製代碼

保證線程安全異步

dispatch_queue_t concurrentQueue = dispatch_queue_create("myQueen", DISPATCH_QUEUE_CONCURRENT);
    for (int i = 0; i<2000; i++) {
        dispatch_async(concurrentQueue, ^{
            NSString *imageName = [NSString stringWithFormat:@"%d.jpg", (i % 10)];
            NSURL *url = [[NSBundle mainBundle] URLForResource:imageName withExtension:nil];
            NSData *data = [NSData dataWithContentsOfURL:url];
            UIImage *image = [UIImage imageWithData:data];
            //若是沒有柵欄函數,會崩潰。
            dispatch_barrier_async(concurrentQueue, ^{
                [self.mArray addObject:image];
            });
        });
    }
複製代碼
調度組

控制任務執行順序
dispatch_group_t group = dispatch_group_create: 建立組
dispatch_queue_t queue = dispatch_group_async: 進組任務
dispatch_group_notify:進組任務執行完畢通知
dispatch_group_wait:進組任務執行等待時間async

//建立調度組
    dispatch_group_t group = dispatch_group_create();
    dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
    dispatch_queue_t queue1 = dispatch_queue_create("myQueue", DISPATCH_QUEUE_CONCURRENT);

    dispatch_group_async(group, queue, ^{
        NSLog(@"任務一");
    });
    
    dispatch_group_async(group, queue1, ^{
        NSLog(@"任務二");
    });
    
    dispatch_group_notify(group, queue, ^{
        NSLog(@"任務結束");
    });
複製代碼

dispatch_group_enter:進組
dispatch_group_leave:出組
調用enter跟leave須要注意,要一一對應。
enter 多於 leave 不會調用通知notify。
enter 少於 leave 就會奔潰。函數

dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
    dispatch_group_t group = dispatch_group_create();
    
    dispatch_group_enter(group);
    dispatch_async(queue, ^{
        NSLog(@"第一個走完了");
        dispatch_group_leave(group);
    });
    
    dispatch_group_enter(group);
    dispatch_async(queue, ^{
        NSLog(@"第二個走完了");
        dispatch_group_leave(group);
    });
    
    dispatch_group_notify(group, dispatch_get_main_queue(), ^{
        NSLog(@"全部任務完成,能夠更新UI");
    });
    
複製代碼
源 Dispatch_source(跟RunLoop的源不同)
@property (nonatomic, strong) dispatch_source_t source;
@property (nonatomic, strong) dispatch_queue_t queue;

- (void)viewDidLoad {
    [super viewDidLoad];
    self.queue = dispatch_queue_create("myQueue", 0);
    self.source = dispatch_source_create(DISPATCH_SOURCE_TYPE_DATA_ADD, 0, 0, dispatch_get_main_queue());

    dispatch_source_set_event_handler(self.source, ^{
    NSUInteger value = dispatch_source_get_data(self.source); // 取回來值 1 響應式
    NSLog(@"響應回來的值 === %lu",value);
});
//默認是掛起的,不resume就不執行
dispatch_resume(self.source);
}
//觸發事件
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    dispatch_source_merge_data(self.source, 1); // source 值響應
}
複製代碼
相關文章
相關標籤/搜索