论坛网站制作费用昆山智能网站建设

当前位置: 首页 > news >正文

论坛网站制作费用,昆山智能网站建设,自建网站和第三方平台,公司微网站建设方案关于GCD和队列的简单介绍请看#xff1a;【iOS】GCD学习 本篇主要介绍GCD中的方法。 栅栏方法:dispatch_barrier_async 我们有时候需要异步执行两组操作#xff0c;而且第一组操作执行完之后#xff0c;才能开始执行第二组操作#xff0c;当然操作组里也可以包含一个或者…关于GCD和队列的简单介绍请看【iOS】GCD学习 本篇主要介绍GCD中的方法。 栅栏方法:dispatch_barrier_async 我们有时候需要异步执行两组操作而且第一组操作执行完之后才能开始执行第二组操作当然操作组里也可以包含一个或者多个任务 这就需要用到dispatch_barrier_async方法在两个操作组间形成栅栏 dispatch_barrier_async方法会等待前边追加到并发队列中的任务全部执行完毕后再将制定的任务追加到该异步队列中。然后在dispatch_barrier_async方法追加的任务执行完毕之后接着追加任务到该异步队列并开始执行大佬博客中的示意图非常的形象具体图示如下 栅栏方法的代码使用样例如下

  • (void) barrier {dispatch_queue_t queue dispatch_queue_create(net.testQueue, DISPATCH_QUEUE_CONCURRENT);dispatch_async(queue, ^{// 追加任务 1[NSThread sleepForTimeInterval:2]; // 模拟耗时操作NSLog(1—%,[NSThread currentThread]); // 打印当前线程});dispatch_async(queue, ^{// 追加任务 2[NSThread sleepForTimeInterval:2]; // 模拟耗时操作NSLog(2—%,[NSThread currentThread]); // 打印当前线程});dispatch_barrier_async(queue, ^{// 追加任务 barrier[NSThread sleepForTimeInterval:2]; // 模拟耗时操作NSLog(barrier—%,[NSThread currentThread]);// 打印当前线程});dispatch_async(queue, ^{// 追加任务 3[NSThread sleepForTimeInterval:2]; // 模拟耗时操作NSLog(3—%,[NSThread currentThread]); // 打印当前线程});dispatch_async(queue, ^{// 追加任务 4[NSThread sleepForTimeInterval:2]; // 模拟耗时操作NSLog(4—%,[NSThread currentThread]); // 打印当前线程});}结果
    在执行完栅栏前面的操作之后执行栅栏操作最后再执行栅栏后面的操作。 GCD中同步栅栏和异步栅栏 我们之前考虑异步栅栏单一队列的时候栅栏只作用于同一队列 那么对于身处不同队列的任务又有什么样的拦截作用呢 对于重要的栅栏方法部分我们将各种情况都实验一下 异步栅栏单一串行队列 由于异步执行串行队列本身就是在创建的唯一一个新线程里按任务添加顺序排队执行所以其实在这种情况下添加栅栏是没有意义的
  • (void) asyncBarrierAndOneSerial {dispatch_queue_t queue dispatch_queue_create(net.testQueue, DISPATCH_QUEUE_SERIAL);dispatch_async(queue, ^{// 追加任务 1[NSThread sleepForTimeInterval:2]; // 模拟耗时操作NSLog(1—%,[NSThread currentThread]); // 打印当前线程});dispatch_async(queue, ^{// 追加任务 2[NSThread sleepForTimeInterval:2]; // 模拟耗时操作NSLog(2—%,[NSThread currentThread]); // 打印当前线程});dispatch_barrier_async(queue, ^{// 追加任务 barrier[NSThread sleepForTimeInterval:2]; // 模拟耗时操作NSLog(barrier—%,[NSThread currentThread]);// 打印当前线程});dispatch_async(queue, ^{// 追加任务 3[NSThread sleepForTimeInterval:2]; // 模拟耗时操作NSLog(3—%,[NSThread currentThread]); // 打印当前线程});dispatch_async(queue, ^{// 追加任务 4[NSThread sleepForTimeInterval:2]; // 模拟耗时操作NSLog(4—%,[NSThread currentThread]); // 打印当前线程}); }结果
    异步栅栏单一并行队列 该种情况上方已经讲述过了 同步栅栏单一串行队列
  • (void)syncBarrierAndOneSerial {dispatch_queue_t queue dispatch_queue_create(net.testQueue, DISPATCH_QUEUE_SERIAL);dispatch_async(queue, ^{// 追加任务1[NSThread sleepForTimeInterval:2]; // 模拟耗时操作NSLog(1–%, [NSThread currentThread]); // 打印当前线程});dispatch_async(queue, ^{// 追加任务2[NSThread sleepForTimeInterval:2]; // 模拟耗时操作NSLog(2–%, [NSThread currentThread]); // 打印当前线程});dispatch_barrier_sync(queue, ^{// 追加任务 barrier[NSThread sleepForTimeInterval:2]; // 模拟耗时操作NSLog(2–%, [NSThread currentThread]); // 打印当前线程});dispatch_async(queue, ^{// 追加任务3[NSThread sleepForTimeInterval:2]; // 模拟耗时操作NSLog(3–%, [NSThread currentThread]); // 打印当前线程});dispatch_async(queue, ^{// 追加任务4[NSThread sleepForTimeInterval:2]; // 模拟耗时操作NSLog(4–%, [NSThread currentThread]); // 打印当前线程}); }结果
    我们可以看到在串行队列中无论是同步执行还是异步执行都是排好队一个一个按顺序来执行的。 同步栅栏单一并行队列
  • (void)syncBarrierAndOneConcurrent {dispatch_queue_t queue dispatch_queue_create(net.testQuquq, DISPATCH_QUEUE_CONCURRENT);dispatch_async(queue, ^{// 追加任务1[NSThread sleepForTimeInterval:2]; // 模拟耗时操作NSLog(1–%, [NSThread currentThread]); // 打印当前线程});dispatch_async(queue, ^{// 追加任务2[NSThread sleepForTimeInterval:2]; // 模拟耗时操作NSLog(2–%, [NSThread currentThread]); // 打印当前线程});dispatch_barrier_sync(queue, ^{// 追加barrier[NSThread sleepForTimeInterval:2]; // 模拟耗时操作NSLog(barrier–%, [NSThread currentThread]); // 打印当前线程});dispatch_async(queue, ^{// 追加任务3[NSThread sleepForTimeInterval:2]; // 模拟耗时操作NSLog(3–%, [NSThread currentThread]); // 打印当前线程});dispatch_async(queue, ^{// 追加任务4[NSThread sleepForTimeInterval:2]; // 模拟耗时操作NSLog(4–%, [NSThread currentThread]); // 打印当前线程}); }运行结果 实际的运行结果是栅栏前的任务组也就是任务1和任务2在程序开始执行两秒之后同时打印了结果接着两秒的时间单独执行了栅栏中的方法最后两秒时间同时执行了栅栏后的任务组也就是任务3和任务4而且由于栅栏前后的任务组中的任务都是在并行队列中异步执行所以执行结束的顺序是不确定的。 异步栅栏多个串行队列
  • (void)asyncBarrierAndSerials {dispatch_queue_t queue1 dispatch_queue_create(net.testQueue1, DISPATCH_QUEUE_SERIAL);dispatch_queue_t queue2 dispatch_queue_create(net.testQueue2, DISPATCH_QUEUE_SERIAL);dispatch_queue_t queue3 dispatch_queue_create(net.testQueue3, DISPATCH_QUEUE_SERIAL);dispatch_queue_t queue4 dispatch_queue_create(net.testQueue4, DISPATCH_QUEUE_SERIAL);dispatch_queue_t queue5 dispatch_queue_create(net.testQueue5, DISPATCH_QUEUE_SERIAL);dispatch_async(queue1, ^{// 追加任务1[NSThread sleepForTimeInterval:2]; // 模拟耗时操作NSLog(1—%, [NSThread currentThread]); // 打印当前线程});dispatch_async(queue2, ^{// 追加任务2[NSThread sleepForTimeInterval:2]; // 模拟耗时操作NSLog(2—%, [NSThread currentThread]); // 打印当前线程});dispatch_barrier_async(queue3, ^{// 追加任务 barrier[NSThread sleepForTimeInterval:2]; // 模拟耗时操作NSLog(barrier—%, [NSThread currentThread]); // 打印当前线程});dispatch_async(queue4, ^{// 追加任务4[NSThread sleepForTimeInterval:2]; // 模拟耗时操作NSLog(4—%, [NSThread currentThread]); // 打印当前线程});dispatch_async(queue5, ^{// 追加任务5[NSThread sleepForTimeInterval:2]; // 模拟耗时操作NSLog(5—%, [NSThread currentThread]); // 打印当前线程}); }结果
    异步栅栏多个串行队列的情况下每个任务都是几乎同时执行的五个任务执行的结束时间都是完全随机的此时的栅栏也就失去了该有的意义。 异步栅栏多个并行队列 异步栅栏多个串行队列情况的各任务执行结束时间都是完全随机的所以异步栅栏多个并行队列更是可想而知肯定也是完全随机的。
  • (void) asyncBarrierAndConcurrents {dispatch_queue_t queueFirst dispatch_queue_create(net.testQueueFirst, DISPATCH_QUEUE_CONCURRENT);dispatch_queue_t queueSecond dispatch_queue_create(net.testQueueSecond, DISPATCH_QUEUE_CONCURRENT);dispatch_queue_t queueThird dispatch_queue_create(net.testQueueThird, DISPATCH_QUEUE_CONCURRENT);dispatch_queue_t queueFourth dispatch_queue_create(net.testQueueFourth, DISPATCH_QUEUE_CONCURRENT);dispatch_queue_t queueFifth dispatch_queue_create(net.testQueueFifth, DISPATCH_QUEUE_CONCURRENT);dispatch_async(queueFirst, ^{// 追加任务 1[NSThread sleepForTimeInterval:2]; // 模拟耗时操作NSLog(1—%,[NSThread currentThread]); // 打印当前线程});dispatch_async(queueSecond, ^{// 追加任务 2[NSThread sleepForTimeInterval:2]; // 模拟耗时操作NSLog(2—%,[NSThread currentThread]); // 打印当前线程});dispatch_barrier_async(queueThird, ^{// 追加任务 barrier[NSThread sleepForTimeInterval:2]; // 模拟耗时操作NSLog(barrier—%,[NSThread currentThread]);// 打印当前线程});dispatch_async(queueFourth, ^{// 追加任务 3[NSThread sleepForTimeInterval:2]; // 模拟耗时操作NSLog(3—%,[NSThread currentThread]); // 打印当前线程});dispatch_async(queueFifth, ^{// 追加任务 4[NSThread sleepForTimeInterval:2]; // 模拟耗时操作NSLog(4—%,[NSThread currentThread]); // 打印当前线程}); }结果
    同步栅栏多个串行队列
  • (void) syncBarrierAndSerials {dispatch_queue_t queueFirst dispatch_queue_create(net.testQueueFirst, DISPATCH_QUEUE_SERIAL);dispatch_queue_t queueSecond dispatch_queue_create(net.testQueueSecond, DISPATCH_QUEUE_SERIAL);dispatch_queue_t queueThird dispatch_queue_create(net.testQueueThird, DISPATCH_QUEUE_SERIAL);dispatch_queue_t queueFourth dispatch_queue_create(net.testQueueFourth, DISPATCH_QUEUE_SERIAL);dispatch_queue_t queueFifth dispatch_queue_create(net.testQueueFifth, DISPATCH_QUEUE_SERIAL);dispatch_async(queueFirst, ^{// 追加任务 1[NSThread sleepForTimeInterval:2]; // 模拟耗时操作NSLog(1—%,[NSThread currentThread]); // 打印当前线程});dispatch_async(queueSecond, ^{// 追加任务 2[NSThread sleepForTimeInterval:2]; // 模拟耗时操作NSLog(2—%,[NSThread currentThread]); // 打印当前线程});dispatch_barrier_sync(queueThird, ^{// 追加任务 barrier[NSThread sleepForTimeInterval:2]; // 模拟耗时操作NSLog(barrier—%,[NSThread currentThread]);// 打印当前线程});dispatch_async(queueFourth, ^{// 追加任务 3[NSThread sleepForTimeInterval:2]; // 模拟耗时操作NSLog(3—%,[NSThread currentThread]); // 打印当前线程});dispatch_async(queueFifth, ^{// 追加任务 4[NSThread sleepForTimeInterval:2]; // 模拟耗时操作NSLog(4—%,[NSThread currentThread]); // 打印当前线程}); }结果
    这种情况下的栅栏和任务1还有任务2是几乎同时执行同时先出结果的(而且每次栅栏都是第一个出结果)但是由于同步的栅栏占用了主线程就导致栅栏后的任务3和任务4只能等到栅栏中的任务执行完成之后再开始去执行。 同步栅栏多个并行队列
  • (void) syncBarrierAndConcurrents {dispatch_queue_t queueFirst dispatch_queue_create(net.testQueueFirst, DISPATCH_QUEUE_CONCURRENT);dispatch_queue_t queueSecond dispatch_queue_create(net.testQueueSecond, DISPATCH_QUEUE_CONCURRENT);dispatch_queue_t queueThird dispatch_queue_create(net.testQueueThird, DISPATCH_QUEUE_CONCURRENT);dispatch_queue_t queueFourth dispatch_queue_create(net.testQueueFourth, DISPATCH_QUEUE_CONCURRENT);dispatch_queue_t queueFifth dispatch_queue_create(net.testQueueFifth, DISPATCH_QUEUE_CONCURRENT);dispatch_async(queueFirst, ^{// 追加任务 1[NSThread sleepForTimeInterval:2]; // 模拟耗时操作NSLog(1—%,[NSThread currentThread]); // 打印当前线程});dispatch_async(queueSecond, ^{// 追加任务 2[NSThread sleepForTimeInterval:2]; // 模拟耗时操作NSLog(2—%,[NSThread currentThread]); // 打印当前线程});dispatch_barrier_sync(queueThird, ^{// 追加任务 barrier[NSThread sleepForTimeInterval:2]; // 模拟耗时操作NSLog(barrier—%,[NSThread currentThread]);// 打印当前线程});dispatch_async(queueFourth, ^{// 追加任务 3[NSThread sleepForTimeInterval:2]; // 模拟耗时操作NSLog(3—%,[NSThread currentThread]); // 打印当前线程});dispatch_async(queueFifth, ^{// 追加任务 4[NSThread sleepForTimeInterval:2]; // 模拟耗时操作NSLog(4—%,[NSThread currentThread]); // 打印当前线程}); }结果
    实际运行过程中任务1、任务2和栅栏都是同时先开始去执行的而且三者执行结束的时间是不确定的然而由于栅栏占用了主线程的原因任务3和任务4只有等到栅栏执行完成之后才开始执行。 延时执行方法dispatch_after 我们经常会遇到这样的需求在指定时间例如 3 秒之后执行某个任务。这种情况就可以用 GCD 的dispatch_after 方法来实现。 需要注意的是dispatch_after 方法并不是在指定时间之后才开始执行处理而是在指定时间之后将任务追加到主队列中。严格来说这个时间并不是绝对准确的但想要大致延迟执行任务dispatch_after 方法是很有效的。
  • (void)after {NSLog(currentThread—%, [NSThread currentThread]); // 打印当前线程NSLog(asyncMain—begin);dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{// NSEC_PER_SEC是一个宏定义通常用于表示一秒钟所包含的纳秒数。// 2.0 秒后异步追加任务代码到主队列并开始执行NSLog(after—%, [NSThread currentThread]); // 打印当前线程NSLog(asyncMain—willEnd);}); }结果
    具体的运行情况是先打印了asyncMain—begin接着过了两秒后紧接着按顺序打印了after—_NSMainThread: 0x60000110c900{number 1, name main}和asyncMain—willEnd。 GCD 一次性代码只执行一次dispatch_once 我们在创建单例、或者有整个程序运行过程中只执行一次的代码时我们就用到了 GCD 的 dispatch_once 方法。使用 dispatch_once 方法能保证某段代码在程序运行过程中只被执行 1 次并且即使在多线程的环境下dispatch_once 也可以保证线程安全。 /*** 一次性代码只执行一次dispatch_once*/
  • (void)once {static dispatch_once_t onceToken;dispatch_once(onceToken, ^{// 只执行 1 次的代码这里面默认是线程安全的}); }GCD 快速迭代方法dispatch_apply 通常我们会用 for 循环遍历但是 GCD 给我们提供了快速迭代的方法dispatch_apply。dispatch_apply 按照指定的次数将指定的任务追加到指定的队列中并等待全部队列执行结束。 如果是在串行队列中使用 dispatch_apply那么就和 for 循环一样按顺序同步执行。但是这样就体现不出快速迭代的意义了。 我们可以利用并发队列进行异步执行。比如说遍历 0~5 这 6 个数字for 循环的做法是每次取出一个元素逐个遍历。dispatch_apply 可以在多个线程中同时异步遍历多个数字。 还有一点无论是在串行队列还是并发队列中dispatch_apply 都会等待全部任务执行完毕这点就像是同步操作也像是队列组中的 dispatch_group_wait方法
  • (void)apply {dispatch_queue_t queue dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);NSLog(apply—begin);dispatch_apply(6, queue, ^(size_t iteration) {NSLog(%zd—%, iteration, [NSThread currentThread]);});NSLog(apply—end); }运行结果如下
    因为是在并发队列中异步执行任务所以各个任务的执行时间长短不定最后结束顺序也不定。但是 apply—end 一定在最后执行。这是因为 dispatch_apply 方法会等待全部任务执行完毕。 GCD 队列组dispatch_group 有时候我们会有这样的需求分别异步执行2个耗时任务然后当2个耗时任务都执行完毕后再回到主线程执行任务。这时候我们可以用到 GCD 的队列组。 调用队列组的 dispatch_group_async 先把任务放到队列中然后将队列放入队列组中。或者使用队列组的 dispatch_group_enter、dispatch_group_leave 组合来实现 dispatch_group_async 调用队列组的 dispatch_group_notify 回到指定线程执行任务。或者使用 dispatch_group_wait 回到当前线程继续向下执行会阻塞当前线程。 dispatch_group_notify 监听 group 中任务的完成状态当所有的任务都执行完成后追加任务到 group 中并执行任务
  • (void)group {dispatch_queue_t queue dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);dispatch_group_t group dispatch_group_create();dispatch_group_async(group, queue, ^{NSLog(blk0);});dispatch_group_async(group, queue, ^{NSLog(blk1);});dispatch_group_async(group, queue, ^{NSLog(blk2);});//dispatch_group_notify会等到group中的处理全部结束时再开始执行//在group中的处理全部结束时将第三个参数block追加到第二个参数所对应的queue中dispatch_group_notify(group, dispatch_get_main_queue(), ^{NSLog(done);}); }运行结果
    由于添加到group中的队列在多线程并发时的执行结果时间是不确定的所以打印的顺序都是随机的(理论如此不过任务的执行顺序可能受到提交的先后顺序的影响尤其是当多个任务都被提交到同一个队列时。)。 dispatch_group_wait 另外我们也可以使用dispatch_group_wait(group, DISPATCH_TIME_FOREVER);第二个参数为dispatch_time_t类型可以自定义来等待group中的处理全部结束 dispatch_group_wait用于暂停当前线程阻塞当前线程等待指定的 group 中的任务执行完成后才会往下继续执行。 如果我们不去添加dispatch_group_wait来进行等待那么由于group中的处理本身也是异步的所以就会在group中的处理还没有执行完时就去执行其他的任务例子如下
  • (void)groupWait {dispatch_queue_t queue dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);dispatch_group_t group dispatch_group_create();dispatch_group_async(group, queue, ^{NSLog(blk0);});dispatch_group_async(group, queue, ^{NSLog(blk1);});dispatch_group_async(group, queue, ^{NSLog(blk2);});NSLog(YES!!); // dispatch_group_wait(group, DISPATCH_TIME_FOREVER); }结果 可以看到打印YES!!操作在group中的处理还没有执行完时就已经执行了。 而像下面这样
  • (void)groupWait {dispatch_queue_t queue dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);dispatch_group_t group dispatch_group_create();dispatch_group_async(group, queue, ^{NSLog(blk0);});dispatch_group_async(group, queue, ^{NSLog(blk1);});dispatch_group_async(group, queue, ^{NSLog(blk2);});dispatch_group_wait(group, DISPATCH_TIME_FOREVER);NSLog(YES!!); }结果
    可以看到就是在group中全部的处理执行完之后再执行的打印YES!!操作 从 dispatch_group_wait 相关代码运行输出结果可以看出 当所有任务执行完成之后才执行 dispatch_group_wait 之后的操作。但是使用dispatch_group_wait 会阻塞当前线程 dispatch_group_enter、dispatch_group_leave dispatch_group_enter 标志着一个任务追加到 group执行一次相当于 group 中未执行完毕任务数 1 dispatch_group_leave 标志着一个任务离开了 group执行一次相当于 group 中未执行完毕任务数 -1 当 group 中未执行完毕任务数为0 的时候才会使 dispatch_group_wait 解除阻塞以及执行追加到 dispatch_group_notify 中的任务 接着我们来看一下通过 dispatch_group_enter 和 dispatch_group_leave 配和来实现向group添加操作
  • (void)groupWithEnterAndLeave {// 首先 需要创建一个线程组dispatch_group_t group dispatch_group_create();// 任务1dispatch_group_enter(group);void (^blockFirst)(int) ^(int a){NSLog(任务%d完成, a);dispatch_group_leave(group);};blockFirst(1);// 任务2dispatch_group_enter(group);void (^blockSecond)(int) ^(int a){NSLog(任务%d完成, a);dispatch_group_leave(group);};blockSecond(2);// 全部完成dispatch_group_notify(group, dispatch_get_main_queue(), ^(){NSLog(全部完成);}); }结果
    我们可以看到任务1和任务2执行完成之后才会执行全部完成中的任务。 从 dispatch_group_enter、dispatch_group_leave 相关代码运行结果中可以看出当所有任务执行完成之后才执行 dispatch_group_notify 中的任务。这里的dispatch_group_enter、dispatch_group_leave 组合其实等同于dispatch_group_async。 不过使用dispatch_group_enter 和 dispatch_group_leave 需要成对出现。 如果 dispatch_group_leave 的调用次数多于 dispatch_group_enter 的调用次数程序会 crash。 GCD 信号量dispatch_semaphore GCD 中的信号量是指 Dispatch Semaphore是持有计数的信号。类似于过高速路收费站的栏杆。可以通过时打开栏杆不可以通过时关闭栏杆。在 Dispatch Semaphore 中使用计数来完成这个功能计数小于 0 时需要等待不可通过。计数为 0 或大于 0 时不用等待可通过。计数大于 0 且计数减 1 时不用等待可通过。 Dispatch Semaphore 提供了三个方法 dispatch_semaphore_create创建一个 Semaphore 并初始化信号的总量。dispatch_semaphore_signal发送一个信号让信号总量加 1。dispatch_semaphore_wait可以使总信号量减 1信号总量小于 0 时就会一直等待阻塞所在线程否则就可以正常执行。 注意 信号量的使用前提是想清楚你需要处理哪个线程等待阻塞又要哪个线程继续执行然后使用信号量。 Dispatch Semaphore 在实际开发中主要用于 保持线程同步将异步执行任务转换为同步执行任务。保证线程安全为线程加锁。 Dispatch Semaphore 线程同步 我们在开发中会遇到这样的需求异步执行耗时任务并使用异步执行的结果进行一些额外的操作。换句话说相当于将将异步执行任务转换为同步执行任务。 下面我们就来利用Dispatch Semaphore实现线程同步将一步执行任务转换为同步执行任务
  • (void)semaphoreSync {NSLog(currentThread—%, [NSThread currentThread]); // 打印当前线程NSLog(semaphore—begin);dispatch_queue_t queue dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);dispatch_semaphore_t semaphore dispatch_semaphore_create(0);__block int number 0;dispatch_async(queue, ^{// 追加任务 1[NSThread sleepForTimeInterval:2]; // 模拟耗时操作NSLog(1—%, [NSThread currentThread]); // 打印当前线程number 100;dispatch_semaphore_signal(semaphore);});dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);NSLog(semaphore—end,number %d, number); }结果
    可以看到semaphore—end 是在执行完 number 100; 之后才打印的。而且输出结果 number 为 100。整个的执行顺序如下 semaphore 初始创建时计数为 0 异步执行 将 任务 1 追加到队列之后不做等待接着执行 dispatch_semaphore_wait 方法semaphore 减 1此时 semaphore -1当前线程进入等待状态后面的内容不执行只执行我们所添加的任务1等到dispatch_semaphore_signal操作使信号量计数0时线程才会恢复正常运作 然后异步任务 1 开始执行。任务 1 执行到 dispatch_semaphore_signal 之后总信号量加 1此时 semaphore 0正在被阻塞的线程主线程恢复继续执行 最后打印 semaphore—end,number 100 这样就实现了线程同步将异步执行任务转换为同步执行任务。 Dispatch Semaphore 线程安全和线程同步为线程加锁 线程安全 如果你的代码所在的进程中有多个线程在同时运行而这些线程可能会同时运行这段代码。如果每次运行结果和单线程运行的结果是一样的而且其他的变量的值也和预期的是一样的就是线程安全的 若每个线程中对全局变量、静态变量只有读操作而无写操作一般来说这个全局变量是线程安全的若有多个线程同时执行写操作更改变量一般都需要考虑线程同步否则的话就可能影响线程安全。 线程同步 可理解为线程 A 和 线程 B 一块配合线程 A 执行到一定程度时要依靠线程B 的某个结果于是停下来示意 线程B 运行线程B 依言执行再将结果给 线程A线程A 再继续操作。 举个简单例子就是两个人在一起聊天。两个人不能同时说话避免听不清(操作冲突)。等一个人说完(一个线程结束操作)另一个再说(另一个线程再开始操作)。 下面我们模拟火车票售卖的方式实现 NSThread 线程安全和解决线程同步问题例子借鉴自大佬博客。 场景 总共有 50 张火车票有两个售卖火车票的窗口一个是北京火车票售卖窗口另一个是上海火车票售卖窗口。两个窗口同时售卖火车票卖完为止。 非线程安全不使用 semaphore 先来看看不考虑线程安全的代码 interface ViewController ()property (nonatomic, assign) NSInteger ticketSurplusCount;end/*** 非线程安全不使用 semaphore* 初始化火车票数量、卖票窗口非线程安全、并开始卖票*/
  • (void)initTicketStatusNotSafe {NSLog(currentThread—%,[NSThread currentThread]); // 打印当前线程NSLog(semaphore—begin);self.ticketSurplusCount 50;// queue1 代表北京火车票售卖窗口dispatch_queue_t queue1 dispatch_queue_create(net.bujige.testQueue1, DISPATCH_QUEUE_SERIAL);// queue2 代表上海火车票售卖窗口dispatch_queue_t queue2 dispatch_queue_create(net.bujige.testQueue2, DISPATCH_QUEUE_SERIAL);__weak typeof(self) weakSelf self;dispatch_async(queue1, ^{[weakSelf saleTicketNotSafe];});dispatch_async(queue2, ^{[weakSelf saleTicketNotSafe];}); }/*** 售卖火车票非线程安全*/
  • (void)saleTicketNotSafe {while (1) {if (self.ticketSurplusCount 0) { // 如果还有票继续售卖self.ticketSurplusCount–;NSLog(%, [NSString stringWithFormat:剩余票数%ld 窗口%, self.ticketSurplusCount, [NSThread currentThread]]);[NSThread sleepForTimeInterval:0.2];} else { // 如果已卖完关闭售票窗口NSLog(所有火车票均已售完);break;}} }结果
    可以看到在不考虑线程安全不使用 semaphore 的情况下得到票数是错乱的而且同一张票可能会发生卖两遍的情况这样显然不符合我们的需求所以我们需要考虑线程安全问题。 线程安全使用 semaphore 加锁 考虑线程安全的代码 interface ViewController ()property (nonatomic, assign) NSInteger ticketSurplusCount;end//创建一个全局信号量 dispatch_semaphore_t semaphoreLock;/*** 线程安全使用 semaphore 加锁* 初始化火车票数量、卖票窗口线程安全、并开始卖票*/
  • (void)initTicketStatusSafe {NSLog(currentThread—%,[NSThread currentThread]); // 打印当前线程NSLog(semaphore—begin);semaphoreLock dispatch_semaphore_create(1);self.ticketSurplusCount 50;// queue1 代表北京火车票售卖窗口dispatch_queue_t queue1 dispatch_queue_create(net.bujige.testQueue1, DISPATCH_QUEUE_SERIAL);// queue2 代表上海火车票售卖窗口dispatch_queue_t queue2 dispatch_queue_create(net.bujige.testQueue2, DISPATCH_QUEUE_SERIAL);__weak typeof(self) weakSelf self;dispatch_async(queue1, ^{[weakSelf saleTicketSafe];});dispatch_async(queue2, ^{[weakSelf saleTicketSafe];}); }/*** 售卖火车票线程安全*/
  • (void)saleTicketSafe {while (1) {// 相当于加锁dispatch_semaphore_wait(semaphoreLock, DISPATCH_TIME_FOREVER);if (self.ticketSurplusCount 0) { // 如果还有票继续售卖self.ticketSurplusCount–;NSLog(%, [NSString stringWithFormat:剩余票数%ld 窗口%, self.ticketSurplusCount, [NSThread currentThread]]);[NSThread sleepForTimeInterval:0.2];} else { // 如果已卖完关闭售票窗口NSLog(所有火车票均已售完);// 相当于解锁dispatch_semaphore_signal(semaphoreLock);break;}// 相当于解锁dispatch_semaphore_signal(semaphoreLock);} }结果
    思路 此处我们采用了dispatch_semaphore 机制买票的操作是每次异步执行的但是如果第一张票还没卖出去第二张票已经开始卖了的话就会由于dispatch_semaphore_wait操作使得信号量计数-1线程就会进入等待状态等待第一张票卖完之后的dispatch_semaphore_signal操作这个操作会让信号量的计数1使得线程重写开始正常运行开始正常执行卖第二张票的处理以此类推通过保护每一次的卖票从而实现整个售票流程的正确性。 可以看出在考虑了线程安全的情况下使用 dispatch_semaphore 机制之后得到的票数是正确的没有出现混乱的情况。我们也就解决了多个线程同步的问题。