一、先来看一个例子
-(void)testSerialQueue
{
dispatch_queue_t queue = dispatch_queue_create("myqueue", DISPATCH_QUEUE_SERIAL);
for (NSInteger i = 0; i < 10; i++) {
dispatch_async(queue, ^{
NSLog(@"%ld",i);
});
}
dispatch_queue_create("myqueue", DISPATCH_QUEUE_SERIAL)
创建了一个dispatch_queue_t
类型的对象,其中:
"myqueue"
是这个queue的标示符,随便输入个字符串就好了·
DISPATCH_QUEUE_SERIAL
标示创建的是一个串行队列。那么并列的还有DISPATCH_QUEUE_CONCURRENT
并行队列,会在下面的例子中看到这两者的区别。
dispatch_async()
有两个参数,参数1是一个queue,这里把我们刚创建的queue写上去,参数2是一个block,block中写我们需要多线程执行的代码。
那么dispatch_async()
的意思是通过async也就是异步的方式将block中的代码提交到参数1中的queue队列中去。
我们执行这个函数,可以看到:
// 2016-02-25 21:03:57.234 FL_GCD_DEMO[4270:3695426] 0
// 2016-02-25 21:03:57.248 FL_GCD_DEMO[4270:3695426] 1
// 2016-02-25 21:03:57.266 FL_GCD_DEMO[4270:3695426] 2
// 2016-02-25 21:03:57.268 FL_GCD_DEMO[4270:3695426] 3
// 2016-02-25 21:03:57.269 FL_GCD_DEMO[4270:3695426] 4
// 2016-02-25 21:03:57.269 FL_GCD_DEMO[4270:3695426] 5
// 2016-02-25 21:03:57.270 FL_GCD_DEMO[4270:3695426] 6
// 2016-02-25 21:03:57.271 FL_GCD_DEMO[4270:3695426] 7
// 2016-02-25 21:03:57.272 FL_GCD_DEMO[4270:3695426] 8
// 2016-02-25 21:03:57.272 FL_GCD_DEMO[4270:3695426] 9
输出是有序的,这好像和我们平时的操作并没有什么区别。
接下来我们尝试使用DISPATCH_QUEUE_CONCURRENT
:
-(void)testConcurrentQueue
{
dispatch_queue_t queue = dispatch_queue_create("myqueue", DISPATCH_QUEUE_CONCURRENT);
for (NSInteger i = 0; i < 10; i++) {
dispatch_async(queue, ^{
NSLog(@"%ld",i);
});
}
}
我们只是更改了create的第二个参数为DISPATCH_QUEUE_CONCURRENT
,这将使创建的队列类型为并发队列。
执行以上代码,输出结果如下:
// 2016-02-25 21:06:04.793 FL_GCD_DEMO[4307:3696674] 2
// 2016-02-25 21:06:04.793 FL_GCD_DEMO[4307:3696672] 0
// 2016-02-25 21:06:04.793 FL_GCD_DEMO[4307:3696673] 1
// 2016-02-25 21:06:04.793 FL_GCD_DEMO[4307:3696684] 3
// 2016-02-25 21:06:04.793 FL_GCD_DEMO[4307:3696685] 4
// 2016-02-25 21:06:04.793 FL_GCD_DEMO[4307:3696686] 5
// 2016-02-25 21:06:04.793 FL_GCD_DEMO[4307:3696674] 6
// 2016-02-25 21:06:04.794 FL_GCD_DEMO[4307:3696672] 7
// 2016-02-25 21:06:04.794 FL_GCD_DEMO[4307:3696673] 8
// 2016-02-25 21:06:04.794 FL_GCD_DEMO[4307:3696684] 9
这个时候我们可以看到输的结果是无序的。这是因为并发队列尽管遵循FIFO(先进先出)的执行顺序,但是由于队列是并发的,我们无法决定代码的具体执行时间。
从上述两个方法我们可以看出DISPATCH_QUEUE_SERIAL
和DISPATCH_QUEUE_CONCURRENT
的区别,他们分别创建的是串行和并发队列
二、来一些经常使用的
有时候我们并不想每次都做创建的动作,而系统也为我们提供了一系列现成的队列供我们使用,我们可以通过以下代码来获得:
dispatch_get_main_queue();
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);
这两个函数会分别返回一个"main_queue"和"global_queue",其中main_queue是系统的主线程,主要用来执行界面更新的操作。而global_queue则是系统的提供的全局线程,我们应当尽量将复杂计算的操作放到global_queue上,如果放在main_queue上则会阻塞主线程,直观的反应就是使界面变卡。
这两个queue的相关测试代码就不写了。
三、dispatch_group
group是什么?顾名思义是“组”的意思。
有时候我们会碰到一些关联的操作,这些关联的操作共同来完成一件事情。比如用户登录的时候需要完成填写账号、填写密码两件事情,才能执行登录操作(当然用户系统肯定不是这么来的...),我们用代码体现下:
-(void)testGroupQueue
{
NSLog(@"用户想要登录");
dispatch_group_t group = dispatch_group_create();
dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
sleep(1);
NSLog(@"填写账号完成");
});
dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
sleep(1);
NSLog(@"填写密码完成");
});
dispatch_group_notify(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
NSLog(@"用户可以登录了");
});
}
执行以上代码,得到的结果如下:
2016-02-26 13:22:09.973 FL_GCD_DEMO[5494:3812823] 用户想要登录
2016-02-26 13:22:10.978 FL_GCD_DEMO[5494:3812859] 填写密码完成
2016-02-26 13:22:10.978 FL_GCD_DEMO[5494:3812860] 填写账号完成
2016-02-26 13:22:10.978 FL_GCD_DEMO[5494:3812860] 用户可以登录了
可以看到,用户“填写账号完成”和“填写密码完成”后,才执行“用户可以登录了”
以上代码中,dispatch_group_create()
创建了一个dispatch_group_t类型的对象group,然后我们把需要放入组操作通过dispatch_group_async()
放到group中,这个函数有三个参数:
参数1:要放入的组对象
参数2:参数3需要放入的队列queue
参数3:需要执行的操作如:NSLog(@"填写密码完成");
我们可以向这个group中放入多个操作。
dispatch_group_notify()
这个函数会在以上放入的所有操作都执行完成后,调用此函数中的参数3操作:NSLog(@"用户可以登录了");
四、dispatch_apply
apply一般会用来异步遍历操作来提高工作效率。
-(void)testApply
{
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);
NSArray *array = @[@1,@2,@3,@4,@5,@6,@6];
size_t count = array.count;
dispatch_apply(count, queue, ^(size_t i) {
NSLog(@"%@",array[i]);
});
}
以上代码的执行结果如下:
2016-02-26 13:37:57.647 FL_GCD_DEMO[5555:3826232] 1
2016-02-26 13:37:57.647 FL_GCD_DEMO[5555:3826274] 2
2016-02-26 13:37:57.647 FL_GCD_DEMO[5555:3826273] 3
2016-02-26 13:37:57.648 FL_GCD_DEMO[5555:3826232] 5
2016-02-26 13:37:57.648 FL_GCD_DEMO[5555:3826274] 6
2016-02-26 13:37:57.648 FL_GCD_DEMO[5555:3826273] 6
2016-02-26 13:37:57.647 FL_GCD_DEMO[5555:3826275] 4
需要注意的是apply会阻塞主线程,如果涉及到大量的数据操作不建议使用apply在主线程上。
五、dispatch_after
dispatch_after用来延后将操作放入队列
-(void)testAfter
{
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);
NSLog(@"开始");
dispatch_async(queue, ^{
NSLog(@"我先来");
});
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), queue, ^{
NSLog(@"我等等");
});
}
执行结果如下:
2016-02-26 13:40:57.665 FL_GCD_DEMO[5611:3829303] 开始
2016-02-26 13:40:57.665 FL_GCD_DEMO[5611:3829339] 我先来
2016-02-26 13:41:00.666 FL_GCD_DEMO[5611:3829339] 我等等
六、Suspend和Resume
有时可能需要我们中断某些操作,还需要我们恢复执行,这个时候就可以用到suspend和resume操作:
-(void)testSuspendAndResume
{
dispatch_queue_t queue = dispatch_queue_create("myqueue", DISPATCH_QUEUE_SERIAL);
NSLog(@"start");
dispatch_async(queue, ^{
[NSThread sleepForTimeInterval:5];
NSLog(@"1");
});
dispatch_async(queue, ^{
[NSThread sleepForTimeInterval:5];
NSLog(@"2");
});
[NSThread sleepForTimeInterval:3];
NSLog(@"suspend");
dispatch_suspend(queue);
[NSThread sleepForTimeInterval:5];
NSLog(@"resume");
dispatch_resume(queue);
}
以上代码执行结果如下:
2016-02-25 22:01:02.475 FL_GCD_DEMO[4890:3722720] start
2016-02-25 22:01:05.476 FL_GCD_DEMO[4890:3722720] suspend
2016-02-25 22:01:07.479 FL_GCD_DEMO[4890:3722759] 1
2016-02-25 22:01:10.478 FL_GCD_DEMO[4890:3722720] resume
2016-02-25 22:01:15.478 FL_GCD_DEMO[4890:3722759] 2
需要注意的是,suspend不会停止当当前正在执行的block,而是中断了后面未执行的,在resume之后再执行后面的block。
七、dispatch_barrier
barrier从字面理解就是障碍物的意思,实际他的功能也是障碍物。
barrier会按照它被加入队列的顺序,隔断前后的操作,使在它之前加入的任务先执行,然后执行barrier,再执行在它之后加入的任务:
-(void)testBarrier
{
dispatch_queue_t queue = dispatch_queue_create("myqueue", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
NSLog(@"1");
});
dispatch_async(queue, ^{
NSLog(@"2");
});
dispatch_barrier_async(queue, ^{
NSLog(@"我在这里隔断了");
});
dispatch_async(queue, ^{
NSLog(@"3");
});
dispatch_async(queue, ^{
NSLog(@"4");
});
}
以上代码的执行结果如下:
2016-02-25 22:13:28.454 FL_GCD_DEMO[5030:3728945] 2
2016-02-25 22:13:28.454 FL_GCD_DEMO[5030:3728942] 1
2016-02-25 22:13:28.455 FL_GCD_DEMO[5030:3728942] 我在这里隔断了
2016-02-25 22:13:28.455 FL_GCD_DEMO[5030:3728945] 4
2016-02-25 22:13:28.455 FL_GCD_DEMO[5030:3728942] 3
八、dispatch_semaphore
semaphore是信号的意思。在我们的当前系统下资源并不是无限的,我们需要控制资源的使用例如控制线程的数量。实际生活中的例子比如下载,我们需要控制允许同时下载的数量。
-(void)testSemaphore
{
dispatch_semaphore_t semaphore = dispatch_semaphore_create(2);
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
for (int i = 0; i <10; i++)
{
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
dispatch_async(queue, ^{
NSLog(@"下载资源:%i",i);
sleep(2);
dispatch_semaphore_signal(semaphore);
});
}
}
运行结果如下:
2016-02-26 14:04:33.392 FL_GCD_DEMO[5713:3848434] 下载资源:1
2016-02-26 14:04:33.392 FL_GCD_DEMO[5713:3848431] 下载资源:0
2016-02-26 14:04:35.397 FL_GCD_DEMO[5713:3848434] 下载资源:2
2016-02-26 14:04:35.397 FL_GCD_DEMO[5713:3848431] 下载资源:3
2016-02-26 14:04:37.400 FL_GCD_DEMO[5713:3848434] 下载资源:5
2016-02-26 14:04:37.400 FL_GCD_DEMO[5713:3848431] 下载资源:4
2016-02-26 14:04:39.401 FL_GCD_DEMO[5713:3848431] 下载资源:6
2016-02-26 14:04:39.401 FL_GCD_DEMO[5713:3848434] 下载资源:7
2016-02-26 14:04:41.406 FL_GCD_DEMO[5713:3848434] 下载资源:9
2016-02-26 14:04:41.406 FL_GCD_DEMO[5713:3848431] 下载资源:8
我们可以这么理解:
dispatch_semaphore_create(2)
创建了一个单位为2的信号量
dispatch_semaphore_wait()
会消耗一个单位
ispatch_semaphore_signal()
会增加一个单位
而dispatch_semaphore_create(2)
中传入的“2”决定了这个信号量最多包含2个单位。
dispatch_semaphore_wait()
中的参数2DISPATCH_TIME_FOREVER
表示当信号量单位变为0个,则使得代码的执行停止在当前位置不再继续下去,直到收到signal使得单位大于0,才继续往下执行。
在上述代码中,每执行一次下载会占用一个单位,下载完成后通过signal告诉wait可以继续向下执行代码,从而控制同时进行的下载数量。
网友评论