参考:
补充队列、任务、同步异步的理解
- 任务:放在队列中的一段代码块,同一个任务中的代码是相继执行的。
- 队列:相当于购物的顾客;串行队列表示需要排队,并行队列是不需要排队的。
- 同步异步函数:相当于服务窗口,一个窗口一个线程。
- 同步是表示就在当前窗口,不给你在开个窗口;
- 异步表示开新窗口,开几个根据任务数了,如果没有任务,就不会开线程了。因为子线程是没有开启runLoop的,任务执行完了,自动会关闭的
主队列是串行队列,全局队列是并行对列
下面代码可以很好的理解任务队列、同步异步的理解
dispatch_async(dispatch_queue_create("d", DISPATCH_QUEUE_CONCURRENT), ^{
NSLog(@"www");
//队列是排队,同步异步是是否开窗口。任务添加到队列。异步等任务执行完才会返回结果,才算完成
sleep(1);
dispatch_sync(dispatch_get_main_queue(), ^{
NSLog(@"fff");
});
NSLog(@"ff");
});
sleep(1);
NSLog(@"fdffdfd");
打印:www;fdffdfd;fff;ff
一、dispatch_timer 定时器
//1.创建队列,并行队列还是主队列看情况
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
//创建定时器
_timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
//设置定时器的两种方式:
1. 只设置时间间隔
dispatch_source_set_timer(_timer, DISPATCH_TIME_NOW, 3 * NSEC_PER_SEC, 0);//1
2. 设置时间间隔为1s,设置开始时间为0s后
dispatch_source_set_timer(_timer, dispatch_time(DISPATCH_TIME_NOW,0), 1 * NSEC_PER_SEC, 0);/
dispatch_source_set_event_handler(_timer, ^{
NSLog(@"fdf");
});
dispatch_resume(_timer);
注:NSEC_PRE_SEC 表示每一秒一纳秒
二、死锁和阻塞线程
1.首先理解几个概念:同步和异步,串行和并发:
-
同步和异步
:同步表示阻塞当前线程,异步表示不阻塞。 -
串行和并发
:是队列,用来添加容纳任务。串行队列,任务是有顺序的,先进先出,任务依次结束。并发,也遵循先进先出,但任务结束顺序呢不定
2.理解队列是如何执行的,什么情况会死锁,什么情况会阻塞线程,举例说明:
注:1. 阻塞线程:同步阻塞。同步函数的block任务必须执行完之后才会返回值 2.添加任务:是添加到指定队列的队尾 3.死锁:代码停了下来,不会往下执行 3.开线程:同步函数是不会开线程的,异步并且非主队列就会开线程 4.并发队列的任务不会相互等待(原因不知:可能是并发执行,不许排队吧)
main{
dispatch_sync(dispatch_get_main_queue(), ^(void){
NSLog(@"这里死锁了");
});
}
//会死锁。首先同步线程,阻塞当前线程(主线程),block任务添加到主队列的结尾,所以block任务会等待同步函数执行完再执行block,但同步函数必须在block执行完之后才会有返回值,block任务又在等同步函数以及同步函数后的任务执行完后才会执行。block和其他主队列中的任务形成死锁。
main{
dispatch_queue_t queue = dispatch_queue_create("d", DISPATCH_QUEUE_SERIAL);
dispatch_sync(queue, ^(void){
NSLog(@"这就不死锁了");
});
}
//不会死锁,会阻塞线程。首先没有开线程,同步就会阻塞线程,因为要等block执行完,才会往下执行。不会死锁是因为任务添加到了另一个非主队列中,主队列只需等待block任务完成就可继续下面的任务,block不需等主队列任务完成。
main{
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
dispatch_sync(queue, ^{
dispatch_sync(queue, ^(void){
NSLog(@"这就不死锁了");
});
NSLog(@"ddd");
});
}
//不会死锁,会阻塞线程。并发任务不用排队,所以不会死锁。
三、group
步骤一
:先通过异步函数将任务都添加到并发队列中dispatch_group_async(group, queue, ^{}
步骤二
:执行结束函数dispatch_group_notify(group, queue, ^{}
//例:异步下载两张图片,下载完成后合成一张图片
- (void)group
{
UIImageView *imageView = [[UIImageView alloc] init];
[self.view addSubview:imageView];
dispatch_group_t group = dispatch_group_create();
dispatch_queue_t queue = dispatch_queue_create("cn.gcd-group.www", DISPATCH_QUEUE_CONCURRENT);
dispatch_group_async(group, queue, ^{
NSLog(@"正在下载第一张图片");
NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:@"http://images2015.cnblogs.com/blog/471463/201509/471463-20150912213125372-589808688.png"]];
NSLog(@"第一张图片下载完毕");
self.imageOne = [UIImage imageWithData:data];
});
dispatch_group_async(group, queue, ^{
NSLog(@"正在下载第二张图片");
NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:@"http://images2015.cnblogs.com/blog/471463/201509/471463-20150912212457684-585830854.png"]];
NSLog(@"第二张图片下载完毕");
self.imageTwo = [UIImage imageWithData:data];
});
dispatch_group_notify(group, queue, ^{
UIGraphicsBeginImageContext(CGSizeMake(300, 400));
[self.imageOne drawInRect:CGRectMake(0, 0, 150, 400)];
[self.imageTwo drawInRect:CGRectMake(150, 0, 150, 400)];
UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
dispatch_async(dispatch_get_main_queue(), ^{
UIImageView *imageView = [[UIImageView alloc] initWithImage:newImage];
[self.view addSubview:imageView];
self.textLabel.text = @"图片合并完毕";
});
});
}
四 dispatch_semaphore_t
dispatch_semaphore
信号量基于计数器的一种多线程同步机制。在多个线程访问共有资源时候,会因为多线程的特性而引发数据出错的问题。
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
dispatch_semaphore_t semaphore = dispatch_semaphore_create(1);
NSMutableArray *array = [NSMutableArrayarray];
for (int index = 0; index < 100000; index++) {
dispatch_async(queue, ^(){
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);//
NSLog(@"addd :%d", index);
[array addObject:[NSNumber numberWithInt:index]];
dispatch_semaphore_signal(semaphore);
});
}
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
如果semaphore
计数大于等于1.计数-1,返回,程序继续运行。如果计数为0,则等待。这里设置的等待时间是一直等待。dispatch_semaphore_signal(semaphore);
计数+1.在这两句代码中间的执行代码,每次只会允许一个线程进入,这样就有效的保证了在多线程环境下,只能有一个线程进入。