串行(serial) ,并发(conCurrent),
任务串行,意味在在同一时间,有且只有一个任务被执行,即一个任务执行完毕后再执行下一个任务。
任务并发,意味着在同一时间,有多个任务被执行。
同步(Syn),异步(Asyn)
同步,意味着在当前线程中执行任务,不具备开启新的线程的功能。
异步,在新的线程中执行任务,具备开启新的线程的功能。
如果在主线程使用一个同步函数,那么有可能阻塞主线程,只有在执行完成这个函数才能继续执行同步函数下面的代码,而使用异步函数则不会阻塞主线程,会开启一个新线程去完成这个函数的任务,
临界区(Critical Section)
就是一段代码不能被并发执行,也就是两个线程不能同时执行这段代码,如果这段代码去操作一个共享资源,一个变量能被并发线程访问,那么这个变量的值已经变的不可信了。
死锁(DeadLock)
所谓的死锁,是指线程卡住了,并等待对方完成或执行其它操作,第一个不能完成是因为在等第二个完成,第二个不能完成是因为在等第一个完成。
dispatch_sync(dispatch_get_main_queue(), ^{
NSLog(@"zhuxianc");
});
执行上面的代码,会发现没有任何打印,这个时候就是发生了死锁,我们禁止在主队列(iOS中主队列是串行队列)中,再同步使用主队列执行任务,同理,禁止在同一个同步串行队列,再使用该同步串行队列同步的执行任务,因为这样会造成死锁。
dispatch_queue_t queue = dispatch_queue_create("abcd", DISPATCH_QUEUE_SERIAL);
dispatch_sync(queue, ^{
NSLog(@"zhuxianc");
dispatch_sync(queue, ^{
NSLog(@"chuanx");
});
NSLog(@"chuanx");
});
NSLog(@"chuanx");
会发现只打印了第一句log zhuxianc, 就死锁了,
线程安全(Thread safe)
线程安全的代码能在多线程或并发任务中被安全的调用,线程不安全的代码在某个时刻只能在一个上下文中运行,一个线程安全的例子就是NSDictionary,你可以在同一个时间多个线程中使用它而不会有问题,而NSMutableDictionary就不是线程安全的,应该保障一次只有一个线程访问它。
队列(queue)
苹果官方对GCD的说明:开发者要做的只是定义想执行的任务追加到适当的
dispatch Queue中
dispatch_queue_t queue = dispatch_queue_create("abcd", DISPATCH_QUEUE_SERIAL);
dispatch_sync(queue, ^{
//想执行的任务
});
这些队列管理你提供给GCD的任务并用先进先出顺序执行这些任务,Dispatch Queue 是执行处理的等待队列,程序员通过dispatch_sync等API,在Block语法中记述想要执行的处理,并将其追加到Dispatch Queue 中,Dispatch Queue按照追加的顺序进行处理,
所有的调度队列(dispatch queue)自身都是线程安全的,你能从多个线程并行的访问它们,
串行队列(serial dispatch queue)
添加进串行队列的任务的执行时机受GCD的控制,唯一能确保的事情是GCD一次只执行一个任务,并且按照我们添加到队列的顺序来执行。
@property (nonatomic,strong)dispatch_queue_t serialQueue;
@property (nonatomic,strong)dispatch_queue_t conCurrentQueue;
- (void)viewDidLoad {
[super viewDidLoad];
self.serialQueue = dispatch_queue_create("abcd", DISPATCH_QUEUE_SERIAL);
self.conCurrentQueue = dispatch_queue_create("efg", DISPATCH_QUEUE_CONCURRENT);
for (int i = 0; i<10; i++) {
[self serialPrint:i];
}
}
- (void)serialPrint :(int)number{
//异步串行
dispatch_async(self.serialQueue, ^{
NSLog(@"%d-%@",number,[NSThread currentThread]);
});
}
- (void)concurrentPrint :(int)number{
//异步并行
dispatch_async(self.conCurrentQueue, ^{
NSLog(@"%d-%@",number,[NSThread currentThread]);
});
}
打印的log
2016-10-13 15:13:28.405 httpsTest[335:69530] 0-<NSThread: 0x137630f10>{number = 2, name = (null)}
2016-10-13 15:13:28.407 httpsTest[335:69530] 1-<NSThread: 0x137630f10>{number = 2, name = (null)}
2016-10-13 15:13:28.407 httpsTest[335:69530] 2-<NSThread: 0x137630f10>{number = 2, name = (null)}
2016-10-13 15:13:28.407 httpsTest[335:69530] 3-<NSThread: 0x137630f10>{number = 2, name = (null)}
2016-10-13 15:13:28.407 httpsTest[335:69530] 4-<NSThread: 0x137630f10>{number = 2, name = (null)}
2016-10-13 15:13:28.407 httpsTest[335:69530] 5-<NSThread: 0x137630f10>{number = 2, name = (null)}
2016-10-13 15:13:28.408 httpsTest[335:69530] 6-<NSThread: 0x137630f10>{number = 2, name = (null)}
2016-10-13 15:13:28.408 httpsTest[335:69530] 7-<NSThread: 0x137630f10>{number = 2, name = (null)}
2016-10-13 15:13:28.408 httpsTest[335:69530] 8-<NSThread: 0x137630f10>{number = 2, name = (null)}
2016-10-13 15:13:28.408 httpsTest[335:69530] 9-<NSThread: 0x137630f10>{number = 2, name = (null)}
开辟了一个新线程,并且按顺序执行的,同一时间只有处理完一个任务再处理下一个任务。
并发队列(concurrent dispatch queue)
在并发队列中的任务能得到的保证是它们会按照被添加的顺序开始执行,但这就是全部的保证了,任务可以由任意顺序完成,你不回去知道何时开始运行下一个任务,或者任意时刻有多少Block在运行,这都完全取决于GCD,
for (int i = 0; i<10; i++) {
[self concurrentPrint:i];
}
打印的log
2016-10-13 15:25:50.098 httpsTest[347:72501] 0-<NSThread: 0x146d636c0>{number = 2, name = (null)}
2016-10-13 15:25:50.098 httpsTest[347:72506] 2-<NSThread: 0x146d73550>{number = 3, name = (null)}
2016-10-13 15:25:50.098 httpsTest[347:72503] 3-<NSThread: 0x146ebaca0>{number = 4, name = (null)}
2016-10-13 15:25:50.099 httpsTest[347:72505] 1-<NSThread: 0x146e0c7a0>{number = 5, name = (null)}
2016-10-13 15:25:50.100 httpsTest[347:72505] 7-<NSThread: 0x146e0c7a0>{number = 5, name = (null)}
2016-10-13 15:25:50.100 httpsTest[347:72505] 8-<NSThread: 0x146e0c7a0>{number = 5, name = (null)}
2016-10-13 15:25:50.100 httpsTest[347:72503] 6-<NSThread: 0x146ebaca0>{number = 4, name = (null)}
2016-10-13 15:25:50.100 httpsTest[347:72505] 9-<NSThread: 0x146e0c7a0>{number = 5, name = (null)}
2016-10-13 15:25:50.100 httpsTest[347:72501] 4-<NSThread: 0x146d636c0>{number = 2, name = (null)}
2016-10-13 15:25:50.100 httpsTest[347:72506] 5-<NSThread: 0x146d73550>{number = 3, name = (null)}
可以看到不再是顺序执行任务,开了多个线程,所谓的“并行执行”就是使用多个线程同时处理多个任务,因为完成任务所需要消耗的时间不同,所以完成任务的最终时间不同
dispatch group
在追加到dispatch queue中的多个任务处理完毕后想执行结束处理,这种需求会经常出现,如果只是使用一个串行队列(serial dispatch queue)时,只要将想执行的处理全部追加到该串行队列中,并在最后追加想要执行的结束处理就可以了,但是在使用conCurrent dispatch queue 时,可能会同时使用多个队列(dispatch queue)时,源代码会变的很复杂,在这种情况下,可以使用dispatch group.
dispatch_group_t group = dispatch_group_create();
dispatch_queue_t queue = dispatch_queue_create("efg", DISPATCH_QUEUE_CONCURRENT);
dispatch_group_async(group, queue, ^{
for (int i = 0; i<100000; i++) {
if (i == 99999) {
NSLog(@"1111111");
}
}
});
dispatch_group_async(group, queue, ^{
NSLog(@"22222");
});
dispatch_group_async(group, queue, ^{
NSLog(@"333333");
});
dispatch_group_notify(group, queue, ^{
NSLog(@"done");
});
打印的log
2016-10-13 15:55:06.187 httpsTest[369:79208] 22222
2016-10-13 15:55:06.187 httpsTest[369:79209] 1111111
2016-10-13 15:55:06.187 httpsTest[369:79213] 333333
2016-10-13 15:55:06.189 httpsTest[369:79213] done
无论向什么样的操作队列(dispatch queue)中追加处理,使用 dispatch group 可以监视这些处理执行的结果,一旦检测到所有处理执行结束,就可以将结束的处理追加到dispatch queue中,这就是使用dispatch group的原因
dispatch_barrier_ async
在访问数据库或者文件时,我们可以使用concurrent dispatch queue 的dispatch_barrier_async可避免数据竞争问题,代码如下所示
确保某个属性线程安全的读写:
@interface TabBarViewController ()
@property(nonatomic,copy)NSString *name;
@end
static NSString *_name;
static dispatch_queue_t queue;
@implementation TabBarViewController
-(instancetype)init{
if (self == [super init]) {
queue = dispatch_queue_create("123456", DISPATCH_QUEUE_CONCURRENT);
}
return self;
}
-(void)setName:(NSString *)name{
dispatch_barrier_async(queue, ^{
_name = [name copy];
});
}
-(NSString *)name{
__block NSString *tenname;
dispatch_sync(queue, ^{
tenname = _name;
});
return tenname;
}