咱們有A、B、C、D四個異步任務,AB執行結束才能執行C,A執行完成才能執行D數組
分兩步實現安全
dispatch_group_t
+dispatch_barrier
就能夠很容易實現dispatch_group_t group = dispatch_group_create();
dispatch_queue_t conQueue = dispatch_queue_create("conQueue", DISPATCH_QUEUE_CONCURRENT);
dispatch_group_async(group, conQueue, ^{
sleep(6);
NSLog(@"taskA");
dispatch_semaphore_signal(semaphore);
});
dispatch_group_async(group, conQueue, ^{
NSLog(@"taskB");
});
dispatch_barrier_async(conQueue, ^{
NSLog(@"taskC");
});
複製代碼
注意要本身建立併發隊列不要使用globalQueue
,這裏有坑點markdown
這一步彷佛就沒那麼好實現,咱們須要引入狀態變量或者鎖才能解決A執行完成才能執行D的問題,咱們能夠使用GCD的信號量來解決網絡
dispatch_group_t group = dispatch_group_create();
dispatch_queue_t conQueue = dispatch_queue_create("conQueue", DISPATCH_QUEUE_CONCURRENT);
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
dispatch_group_async(group, conQueue, ^{
sleep(6);
NSLog(@"taskA");
dispatch_semaphore_signal(semaphore);
});
dispatch_group_async(group, conQueue, ^{
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
NSLog(@"taskD");
});
dispatch_group_async(group, conQueue, ^{
NSLog(@"taskB");
});
dispatch_barrier_async(conQueue, ^{
NSLog(@"taskC");
});
複製代碼
NSBlockOperation *taskA = [NSBlockOperation blockOperationWithBlock:^{
sleep(6);
NSLog(@"taskA");
}];
NSBlockOperation *taskB = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"taskB");
}];
NSBlockOperation *taskC = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"taskC");
}];
NSBlockOperation *taskD = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"taskD");
}];
[taskC addDependency:taskA];
[taskC addDependency:taskB];
[taskD addDependency:taskA];
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
[queue addOperation:taskA];
[queue addOperation:taskB];
[queue addOperation:taskC];
[queue addOperation:taskD];
複製代碼
只須要添加這幾行任務依賴關係就能夠了多線程
[taskC addDependency:taskA];
[taskC addDependency:taskB];
[taskD addDependency:taskA];
複製代碼
這樣看來NSOperation、NSOperationQueue比GCD更加的面向對象,而不須要經過信號量這種過程變量來控制。併發
GCD已經能知足咱們解決線程問題的須要,NSOperation、NSOperationQueue是基於GCD的一層封裝,更加面向對象,使用起來更加簡單,在解決諸如任務的依賴時尤其簡單。異步
參考文章 iOS 多線程:『NSOperation、NSOperationQueue』詳盡總結async
NSOperation、NSOperationQueue是基於CGD的封裝,那麼NSOperation、NSOperationQueue一樣有任務和隊列的概念oop
操做(Operation):spa
操做隊列(Operation Queues):
NSInvocationOperation *operation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(fun) object:nil];
NSInvocationOperation *operation1 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(fun1) object:nil];
[operation start];
[operation1 start];
複製代碼
能夠看到NSInvocationOperation
是在當前線程同步執行,須要手動調起start
方法
NSBlockOperation
NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
sleep(3);
NSLog(@"fun");
}];
NSBlockOperation *operation1 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"fun1");
}];
[operation start];
[operation1 start];
複製代碼
和NSInvocationOperation
相似,也是在當前線程同步執行,也須要手動調用start
方法,另外還能夠經過``addExecutionBlock`添加任務
NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"fun-%@",[NSThread currentThread]);
}];
[operation addExecutionBlock:^{
NSLog(@"exectionBLock1-%@",[NSThread currentThread]);
}];
[operation addExecutionBlock:^{
NSLog(@"exectionBLock2-%@",[NSThread currentThread]);
}];
[operation addExecutionBlock:^{
NSLog(@"exectionBLock3-%@",[NSThread currentThread]);
}];
[operation addExecutionBlock:^{
NSLog(@"exectionBLock4-%@",[NSThread currentThread]);
}];
[operation addExecutionBlock:^{
NSLog(@"exectionBLock5-%@",[NSThread currentThread]);
}];
[operation addExecutionBlock:^{
NSLog(@"exectionBLock6-%@",[NSThread currentThread]);
}];
[operation addExecutionBlock:^{
NSLog(@"exectionBLock7-%@",[NSThread currentThread]);
}];
[operation addExecutionBlock:^{
NSLog(@"exectionBLock8-%@",[NSThread currentThread]);
}];
[operation start];
複製代碼
經過打印結果能夠看出經過這兩種方式添加的任務是異步執行的,他們均可能在子線程中執行,注意:經過blockOperationWithBlock設置的任務也不必定在主線程(當前線程)中執行
NSOperationQueue 一共有兩種隊列:主隊列、自定義隊列。其中自定義隊列同時包含了串行、併發功能。主隊列必定在主線程執行,執行順序爲串行同步執行
NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"fun-%@",[NSThread currentThread]);
}];
[[NSOperationQueue mainQueue] addOperation:operation];
複製代碼
必定在主線程執行
NSBlockOperation *operation1 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"fun1-%@",[NSThread currentThread]);
}];
NSBlockOperation *operation2 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"fun2-%@",[NSThread currentThread]);
}];
NSBlockOperation *operation3 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"fun3-%@",[NSThread currentThread]);
}];
NSBlockOperation *operation4 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"fun4-%@",[NSThread currentThread]);
}];
NSBlockOperation *operation5 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"fun5-%@",[NSThread currentThread]);
}];
NSBlockOperation *operation6 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"fun6-%@",[NSThread currentThread]);
}];
NSBlockOperation *operation7 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"fun7-%@",[NSThread currentThread]);
}];
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
[queue addOperation:operation1];
[queue addOperation:operation2];
[queue addOperation:operation3];
[queue addOperation:operation4];
[queue addOperation:operation5];
[queue addOperation:operation6];
[queue addOperation:operation7];
複製代碼
能夠看到自定義隊列的任務必定在子線程併發執行,具備開啓新線程的能力
經過maxConcurrentOperationCount
能夠控制隊列的最大併發操做數量,默認值爲-1,表示不進行限制,最大開啓併發數由系統控制,若是設置爲1那就是串行執行,若是設置爲大於1那麼就取 min{自定義值,系統設定最大值},即便咱們設置了一個很大的值,系統也不會無限制的開啓如此多的併發操做.
NSBlockOperation *operation1 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"fun1-%@",[NSThread currentThread]);
}];
NSBlockOperation *operation2 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"fun2-%@",[NSThread currentThread]);
}];
NSBlockOperation *operation3 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"fun3-%@",[NSThread currentThread]);
}];
NSBlockOperation *operation4 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"fun4-%@",[NSThread currentThread]);
}];
NSBlockOperation *operation5 = [NSBlockOperation blockOperationWithBlock:^{
sleep(1);
NSLog(@"fun5-%@",[NSThread currentThread]);
}];
NSBlockOperation *operation6 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"fun6-%@",[NSThread currentThread]);
}];
NSBlockOperation *operation7 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"fun7-%@",[NSThread currentThread]);
}];
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
queue.maxConcurrentOperationCount = 1;
[queue addOperation:operation1];
[queue addOperation:operation2];
[queue addOperation:operation3];
[queue addOperation:operation4];
[queue addOperation:operation5];
[queue addOperation:operation6];
[queue addOperation:operation7];
複製代碼
前面問題中咱們就用了操做依賴addDependency
,例如operation3
依賴operation1
和operation2
NSBlockOperation *operation1 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"fun1-%@",[NSThread currentThread]);
}];
NSBlockOperation *operation2 = [NSBlockOperation blockOperationWithBlock:^{
sleep(1);
NSLog(@"fun2-%@",[NSThread currentThread]);
}];
NSBlockOperation *operation3 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"fun3-%@",[NSThread currentThread]);
}];
[operation3 addDependency:operation1];
[operation3 addDependency:operation2];
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
[queue addOperation:operation1];
[queue addOperation:operation2];
[queue addOperation:operation3];
複製代碼
operation1
和operation2
執行結束纔會執行operation3
優先級有五種
typedef NS_ENUM(NSInteger, NSOperationQueuePriority) {
NSOperationQueuePriorityVeryLow = -8L,
NSOperationQueuePriorityLow = -4L,
NSOperationQueuePriorityNormal = 0,
NSOperationQueuePriorityHigh = 4,
NSOperationQueuePriorityVeryHigh = 8
};
複製代碼
例如上面的例子咱們將operation1
優先級設置爲veryLow
,將operation3
優先級設置爲veryHigh
,那麼是否是operation3
會比operation1
更早執行呢????
NSBlockOperation *operation1 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"fun1-%@",[NSThread currentThread]);
}];
NSBlockOperation *operation2 = [NSBlockOperation blockOperationWithBlock:^{
sleep(1);
NSLog(@"fun2-%@",[NSThread currentThread]);
}];
NSBlockOperation *operation3 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"fun3-%@",[NSThread currentThread]);
}];
[operation3 addDependency:operation1];
[operation3 addDependency:operation2];
[operation1 setQueuePriority:NSOperationQueuePriorityVeryLow];
[operation3 setQueuePriority:NSOperationQueuePriorityVeryHigh];
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
[queue addOperation:operation1];
[queue addOperation:operation2];
[queue addOperation:operation3];
複製代碼
能夠看到仍然按照依賴關係先執行,優先級只是控制就緒狀態的任務的開始執行順序,注意是開始執行順序,而不是結束執行順序,先開始的任務不必定先結束,那麼什麼是就緒狀態呢??
例如操做C依賴操做A和操做B,那麼當AB直接結束以前C是處於非就緒狀態的,操做A和操做B能夠同時處於就緒狀態,這個時候若是A的優先級高於B那麼A就比B先開始執行,可是不必定比B早結束執行
例如,網絡耗時操做在子線程執行,結束以後在主線程刷新頁面
NSBlockOperation *operation1 = [NSBlockOperation blockOperationWithBlock:^{
sleep(3);//模擬耗時操做
NSLog(@"fun1-%@",[NSThread currentThread]);
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
//刷新頁面
NSLog(@"fun2-%@",[NSThread currentThread]);
}];
}];
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
[queue addOperation:operation1];
複製代碼
能夠加索保證線程安全
dispatch_semaphore_t semaphore = dispatch_semaphore_create(1);
NSBlockOperation *operation1 = [NSBlockOperation blockOperationWithBlock:^{
while (1) {
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
if (num>0) {
num--;
NSLog(@"%d-%@",num,[NSThread currentThread]);
}
dispatch_semaphore_signal(semaphore);
if (num<=0) {
break;
}
}
}];
NSBlockOperation *operation2 = [NSBlockOperation blockOperationWithBlock:^{
while (1) {
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
if (num>0) {
num--;
NSLog(@"%d-%@",num,[NSThread currentThread]);
}
dispatch_semaphore_signal(semaphore);
if (num<=0) {
break;
}
}
}];
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
[queue addOperation:operation1];
[queue addOperation:operation2];
複製代碼
- (void)cancel;
可取消操做,實質是標記 isCancelled 狀態。- (BOOL)isFinished;
判斷操做是否已經結束。- (BOOL)isCancelled;
判斷操做是否已經標記爲取消。- (BOOL)isExecuting;
判斷操做是否正在在運行。- (BOOL)isReady;
判斷操做是否處於準備就緒狀態,這個值和操做的依賴關係相關。- (void)waitUntilFinished;
阻塞當前線程,直到該操做結束。可用於線程執行順序的同步。- (void)setCompletionBlock:(void (^)(void))block;
completionBlock
會在當前操做執行完畢時執行 completionBlock。- (void)addDependency:(NSOperation *)op;
添加依賴,使當前操做依賴於操做 op 的完成。- (void)removeDependency:(NSOperation *)op;
移除依賴,取消當前操做對操做 op 的依賴。@property (readonly, copy) NSArray<NSOperation *> *dependencies;
在當前操做開始執行以前完成執行的全部操做對象數組。取消/暫停/恢復操做
- (void)cancelAllOperations;
能夠取消隊列的全部操做。- (BOOL)isSuspended;
判斷隊列是否處於暫停狀態。 YES 爲暫停狀態,NO 爲恢復狀態。- (void)setSuspended:(BOOL)b;
可設置操做的暫停和恢復,YES 表明暫停隊列,NO 表明恢復隊列。操做同步
- (void)waitUntilAllOperationsAreFinished;
阻塞當前線程,直到隊列中的操做所有執行完畢。添加/獲取操做`
- (void)addOperationWithBlock:(void (^)(void))block;
向隊列中添加一個 NSBlockOperation 類型操做對象。- (void)addOperations:(NSArray *)ops waitUntilFinished:(BOOL)wait;
向隊列中添加操做數組,wait 標誌是否阻塞當前線程直到全部操做結束- (NSArray *)operations;
當前在隊列中的操做數組(某個操做執行結束後會自動從這個數組清除)。- (NSUInteger)operationCount;
當前隊列中的操做數。獲取隊列
+ (id)currentQueue;
獲取當前隊列,若是當前線程不是在 NSOperationQueue 上運行則返回 nil。+ (id)mainQueue;
獲取主隊列。注意:
- 這裏的暫停和取消(包括操做的取消和隊列的取消)並不表明能夠將當前的操做當即取消,而是噹噹前的操做執行完畢以後再也不執行新的操做。
- 暫停和取消的區別就在於:暫停操做以後還能夠恢復操做,繼續向下執行;而取消操做以後,全部的操做就清空了,沒法再接着執行剩下的操做。