相關文章:
iOS多線程之NSThread
iOS多線程之NSOperations
隊列
| 隊列 | Api | 備注 |
|---|---|---|
| 主隊列(main queue) | dispatch_get_main_queue() |
串行隊列,可以操縱UI |
| 全局調(diào)度隊列(Global Dispatch Queues) | dispatch_get_global_queue() |
并行隊列,按照執(zhí)行優(yōu)先級,分成4種global queue:DISPATCH_QUEUE_PRIORITY_HIGH, DISPATCH_QUEUE_PRIORITY_DEFAULT, DISPATCH_QUEUE_PRIORITY_LOW, DISPATCH_QUEUE_PRIORITY_BACKGROUND
|
| 自創(chuàng)建隊列 | dispatch_queue_create() |
可以創(chuàng)建: 串行( DISPATCH_QUEUE_SERIAL)、并行( DISPATCH_QUEUE_CONCURRENT)隊列 |
| 并行隊列 | 串行隊列 |
|---|---|
![]() 并行隊列
|
![]() 串行隊列
|
隊列和線程是兩個不同的概念。一個隊列可以有多個線程。每個隊列中的操作會在所屬的線程中運行。舉個例子你創(chuàng)建一個并行隊列,然后添加三個操作到里面。隊列會發(fā)起三個單獨的線程,然后讓所有操作在各自的線程中并發(fā)運行。
提交任務(block)到隊列(queue)
| 提交方式 | 備注 |
|---|---|
dispatch_async(dispatch_queue_t queue, dispatch_block_t block) |
異步提交block到queue |
dispatch_sync(dispatch_queue_t queue, dispatch_block_t block) |
同步地提交工作并在返回前等待它完成 |
dispatch_after(dispatch_time_t when, dispatch_queue_t queue, dispatch_block_t block) |
異步提交block到queue,并且延遲執(zhí)行block,dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC))表示3秒后 |
dispatch_barrier_async(dispatch_queue_t queue, dispatch_block_t block) |
異步提交block,但是該block被執(zhí)行時,隊列中其它block不會被執(zhí)行,即barrier相當于一個狹窄的通道 場景:多線程讀寫競態(tài)資源,多個讀線程間可以并行,但讀寫、寫寫線程間只能串行,這時可以: 1)使用并發(fā)隊列(為了防止 barrier特性影響其它線程,不要使用dispatch_get_global_queue,而是使用dispatch_queue_create來創(chuàng)建新隊列)2)使用 dispatch_barrier_async添加寫block,保證隊列中寫block執(zhí)行時不會有其它讀寫block正在執(zhí)行 |

barrier執(zhí)行模型
注意:
如果你調(diào)用 dispatch_sync 并放在你已運行著的當前串行隊列。這會導致死鎖,因為調(diào)用會一直等待直到 Block 完成,但 Block 不能完成(它甚至不會開始?。?,直到當前已經(jīng)存在的任務完成,而當前任務無法完成!舉個例子:
@implementation ViewController1
- (void)viewDidLoad
{
[super viewDidLoad];
// dispatch_sync同步提交block到main隊列(當前隊列)并**等待**block執(zhí)行完畢,而由于是串行隊列,block需要等待當前任務執(zhí)行完畢,雙向等待造成死鎖。
dispatch_sync(dispatch_get_main_queue(), ^{
NSLog(@"hello world");
});
// 下面這條NSLog不會被執(zhí)行,因為線程已經(jīng)死鎖
NSLog(@"here");
@end
@implementation ViewController2
- (void)viewDidLoad
{
[super viewDidLoad];
// 創(chuàng)建一個并行隊列,,,,(*注意:*如果將*DISPATCH_QUEUE_CONCURRENT*修改成*DISPATCH_QUEUE_SERIAL*,即創(chuàng)建串行隊列,就會發(fā)生死鎖!?。。? dispatch_queue_t queue = dispatch_queue_create("abc", DISPATCH_QUEUE_CONCURRENT);
dispatch_sync(queue, ^{
NSLog(@"..1");
// 因為是并行隊列,下面的block不需要等待當前block執(zhí)行完畢,就不會發(fā)生死鎖。
dispatch_sync(queue, ^{
NSLog(@"..2");
});
});
NSLog(@"here");
@end
單例
dispatch_once() 以線程安全的方式執(zhí)行且僅執(zhí)行其代碼塊一次。試圖訪問臨界區(qū)(即傳遞給 dispatch_once 的代碼)的不同的線程會在臨界區(qū)已有一個線程的情況下被阻塞,直到臨界區(qū)完成為止。
@implementation User
+ (instancetype)sharedUser
{
static User *instance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
instance = [[User alloc] init];
});
return instance;
}
@end
調(diào)度組
Dispatch Group 會在整個組的任務都完成時通知你。這些任務可以是同步的,也可以是異步的,即便在不同的隊列也行。而且在整個組的任務都完成時,Dispatch Group 可以用同步的或者異步的方式通知你。
| 創(chuàng)建group Api | 備注 |
|---|---|
dispatch_group_t dispatch_group_create(void) |
創(chuàng)建一個group |
| 將任務(block)添加到group | 備注 |
|---|---|
dispatch_group_async(dispatch_group_t group,dispatch_queue_t queue, dispatch_block_t block) |
異步把block提交到queue,并且將block分配到指定的group |
dispatch_group_enter(dispatch_group_t group) |
手動表明block進入group,dispatch_group_enter與dispatch_group_leave是成對出現(xiàn)的。 |
dispatch_group_leave(dispatch_group_t group) |
手動表明block在group中執(zhí)行完成 |
| 設置group完成時的通知/回調(diào) | 備注 |
|---|---|
dispatch_group_wait(dispatch_group_t group, dispatch_time_t timeout) |
阻塞當前線程,直到group里面所有的任務都完成或者等到某個超時發(fā)生 |
dispatch_group_notify(dispatch_group_t group, dispatch_queue_t queue, dispatch_block_t block) |
異步設置group里所有任務都完成時,在queue隊列中執(zhí)行通知回調(diào)block |
多圖片下載示例:
dispatch_group_t group = dispatch_group_create();
dispatch_queue_t queue = dispatch_queue_create("test", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
for(NSInteger i = 0; i < 10; i++)
{
NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:@"http://a/%zd.png", i]];
// 表示block進入group
dispatch_group_enter(group);
[[SDWebImageDownloader sharedDownloader] downloadImageWithURL:url options:0 progress:nil completed:^(UIImage *image, NSData *data, NSError *error, BOOL finished) {
// 表示block執(zhí)行完成
dispatch_group_leave(group);
}];
}
// 設置等待3秒鐘
dispatch_time_t timeoutTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC));
if(dispatch_group_wait(group, timeoutTime))
{
NSLog(@"圖片下載超時");
}
else
{
NSLog(@"所有圖片都下載完成");
}
});
信號量
信號量讓你控制多個消費者對有限數(shù)量資源的訪問
原則:當信號量小于等于0時,dispatch_semaphore_wait會阻塞線程,當dispatch_semaphore_signal時會讓信號量加1,如果這時信號量大于0,則喚醒阻塞的線程。
| Api | 備注 |
|---|---|
dispatch_semaphore_t dispatch_semaphore_create(long value) |
創(chuàng)建一個信號量,并且設置信號量的初始值 |
dispatch_semaphore_wait(dispatch_semaphore_t dsema, dispatch_time_t timeout) |
等待一個信號量,如果當前信號量大于0,則信號量減1,線程往下執(zhí)行。否則線程阻塞,直到被信號量大于0時被喚醒或者等待超時 |
dispatch_semaphore_signal(dispatch_semaphore_t dsema) |
讓信號量加1,如果當前信號量大于0,則喚醒一個waiting的線程 |
圖片下載示例:
// 創(chuàng)建信號量,并且初始化為0
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
// 下載圖片
[[SDWebImageDownloader sharedDownloader] downloadImageWithURL:[NSURL URLWithString:@"http://a/b.png"] options:0 progress:nil completed:^(UIImage *image, NSData *data, NSError *error, BOOL finished) {
// 設置信號量+1,此時信號量大于0,會喚醒等待的線程。
dispatch_semaphore_signal(semaphore);
}];
// 設置等待3秒鐘
dispatch_time_t timeoutTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 \\* NSEC_PER_SEC));
// 此時線程會阻塞,只到圖片下載完成或者等待超時
if(dispatch_semaphore_wait(semaphore, timeoutTime))
{
NSLog(@"等待超時");
}
else
{
NSLog(@"圖片下載完畢");
}

