iOS多线程GCD(三) 栅栏函数、信号量 、调度组、dispatch_source

栅栏函数

  • 控制任务的执行顺序,导致同步的效果

  • 栅栏函数有两种:

    • dispatch_barrier_async
    • dispatch_barrier_sync
  • di线程撕裂者spatch_barrier_async异步栅栏函数拦住的是同一个线程中的任务,并变量与函数阻塞线程

  • dispatch_barrier_sync同步栅变量的定义栏函数也拦住的是同一个队列中的任务,但阻塞线程

全局队列栅栏

在全局队列中使用异步栅栏函数,什么也没有拦住 流程图分析

iOS多线程GCD(三) 栅栏函数、信号量 、调度组、dispatch_source

总结

    1. 栅栏函数可以阻塞当前线程的任务,达到控制任务的效果,但只能在创建线程池变量与函数线程池面试题发队列中使用
    1. 栅栏函数也可以在多读单写的场景中使用
    1. 栅栏函数只能在当前线程使用,如果多个线程就会起不到想要的效果

信号量

  • 二元信号量它只有01两个状态,多元信号量变量信号量
  • GCD中的信号量disp源码1688atch_semaphore_t中主要有三个函数:
    • dispatch_semaphore_create:创建信号
    • dispatch_semaphore_wait:等待信号
    • dispatch_semaphore_signal:释放信号监控app下载

源码解读

dispatch_semaphore_create

iOS多线程GCD(三) 栅栏函数、信号量 、调度组、dispatch_source

  • 首先如果信号为小于0,则返回一个DISPATCH_BAD_INPUT类型对象,也就是返回个_Nonnull
  • 如果信号大于等于0,就会对dispatch_semaphore_t对象dsema进行一些赋值,并返回源码编辑器下载dsema对象

dispatch_semaphore_wait

intptr_t
dispatch_semaphore_wait(dispatch_semaphore_t dsema, dispatch_time_t timeout)
{
    long value = os_atomic_dec2o(dsema, dsema_value, acquire);
    if (likely(value >= 0)) {
	return 0;
    }
    return _dispatch_semaphore_wait_slow(dsema, timeout);
}
复制代码
  • 等待信号主要是通过os_ato变量名mic_dec2o线程撕裂者函数对信号进行自减,当源码1688值大于等于0时返回0
  • 当值小于0时,会走_dispa源码编程器tch_semaphore_wait_sl变量之间的关系ow方法
_dispatch_semaph线程的几种状态ore_wait_slow

iOS多线程GCD(三) 栅栏函数、信号量 、调度组、dispatch_source

  • 当设置了timeout(超时线程数是什么时间),则会根据类型进行源码之家相关操作,本文使用的是DISPATCH_TIME_FOREVER,此时调用_dispatch_sema4_wait进行等待处理:

    iOS多线程GCD(三) 栅栏函数、信号量 、调度组、dispatch_source

    • 这里主要是一个do-while循环里队等待信号,当不满足条件后才会跳出循环,所以会出现一个等待的效果

dispatch监控安装_semaphore_signal

intptr_t
dispatch_semaphore_signal(dispatch_semaphore_t dsema)
{
    long value = os_atomic_inc2o(dsema, dsema_value, release);
    if (likely(value > 0)) {
	return 0;
    }
    if (unlikely(value == LONG_MIN)) {
	DISPATCH_CLIENT_CRASH(value,
		"Unbalanced call to dispatch_semaphore_signal()");
    }
    return _dispatch_semaphore_signal_slow(dsema);
}
复制代码
  • 发送信号主要监控可以保存多少天是通过os_atomic_inc2源码网站o对信号进行自增,如果自增后的结果大于0,就返回0
  • 如果自增后还线程池是小于0,就会走到_dispatch_semaphore_signal_slow方法
_dispatch_semaphore_sig源码交易平台nal_slow
intptr_t
_dispatch_semaphore_signal_slow(dispatch_semaphore_t dsema)
{
     _dispatch_sema4_create(&dsema->dsema_sema, _DSEMA4_POLICY_FIFO);
     _dispatch_sema4_signal(&dsema->dsema_sema, 1);
     return 1;
}
  • 这里有个_dispatch_sema4_signal函数进行缓慢发送信号

iOS多线程GCD(三) 栅栏函数、信号量 、调度组、dispatch_source

调度组

  • 调度组最直接的作用就是:控制任务的执行顺序Api主要有以下几个方法:
    • dispatch_group_create:创建组
    • dispatch_group_async:进组任务
    • dispatch_group_notify:组任务执行完毕的通知
    • dispatch_group_enter:进组
    • dispatch_group_leave:出组
    • dispatch_group_wa线程数越多越好吗it:等待组任务时间

dispatch_线程数越多越好吗group_async

    1. 调度组不会阻塞线程
    1. 组任务执行没有顺序,相当于异步并发队列线程数是什么
    1. 组任务执行完后才会执行dispatch_group_notify变量之间的关系任务

dispatch_group_wait

dispatch_group_wait的作源码之家用是阻塞调度组之外的任务

    1. 当等待时间结束时,组任务还没完成,就结束阻塞执行其他任务
    1. 当组任务完成,等待时间还未结束时,会结束监控摄像头品牌排行阻塞执行其他任务

原理分析

dispatch_group监控可以保存多少天_creat监控摄像头

dispatch_group_t
dispatch_group_create(void)
{
    return _dispatch_group_create_with_count(0);
}
复制代码

dispatch_group_create方法会调用_dis监控摄像头品牌排行patch_group_create_with_cou变量泵nt方法,并传入参数0

static inline dispatch_group_t
_dispatch_group_create_with_count(uint32_t n)
{
     dispatch_group_t dg = _dispatch_object_alloc(DISPATCH_VTABLE(group),
	      sizeof(struct dispatch_group_s));
     dg->do_next = DISPATCH_OBJECT_LISTLESS;
     dg->do_targetq = _dispatch_get_default_queue(false);
     if (n) {
	 os_atomic_store2o(dg, dg_bits,
		  (uint32_t)-n * DISPATCH_GROUP_VALUE_INTERVAL, relaxed);
	 os_atomic_store2o(dg, do_ref_cnt, 1, relaxed); // <rdar://22318411>
     }
     return dg;
}
复制代码
  • 方法的源码编辑器下载核心是创建d监控系统is源码时代patch_group_t对象dg线程池面试题并对它的do_nextdo_targetq参数进行赋值,然后调用os_atomic_store2o进行存储

dispatch_group_enter

void
dispatch_group_enter(dispatch_group_t dg)
{
    // The value is decremented on a 32bits wide atomic so that the carry
    // for the 0 -> -1 transition is not propagated to the upper 32bits.
    uint32_t old_bits = os_atomic_sub_orig2o(dg, dg_bits,
	     DISPATCH_GROUP_VALUE_INTERVAL, acquire);
    uint32_t old_value = old_bits & DISPATCH_GROUP_VALUE_MASK;
    if (unlikely(old_value == 0)) {
	_dispatch_retain(dg); // <rdar://problem/22318411>
    }
    if (unlikely(old_value == DISPATCH_GROUP_VALUE_MAX)) {
	DISPATCH_CLIENT_CRASH(old_bits,
		 "Too many nested calls to dispatch_group_enter()");
    }
}
复制代码

主要调用os_atomic_su监控家用远程手机b_orig2o进行自减操作,也就是由0 -> -1,这块和线程池面试题信号量的操作很像,但没有wait的步骤

dispatc变量的定义h_grou线程安全p_leave

void
dispatch_group_leave(dispatch_group_t dg)
{
     // The value is incremented on a 64bits wide atomic so that the carry for
     // the -1 -> 0 transition increments the generation atomically.
     uint64_t new_state, old_state = os_atomic_add_orig2o(dg, dg_state,
	      DISPATCH_GROUP_VALUE_INTERVAL, release);
     uint32_t old_value = (uint32_t)(old_state & DISPATCH_GROUP_VALUE_MASK);
     if (unlikely(old_value == DISPATCH_GROUP_VALUE_1)) {
	 old_state += DISPATCH_GROUP_VALUE_INTERVAL;
	 do {
	     new_state = old_state;
	     if ((old_state & DISPATCH_GROUP_VALUE_MASK) == 0) {
		 new_state &= ~DISPATCH_GROUP_HAS_WAITERS;
		 new_state &= ~DISPATCH_GROUP_HAS_NOTIFS;
	     } else {
		 // If the group was entered again since the atomic_add above,
		 // we can't clear the waiters bit anymore as we don't know for
		 // which generation the waiters are for
		 new_state &= ~DISPATCH_GROUP_HAS_NOTIFS;
	     }   
	     if (old_state == new_state) break;
         } while (unlikely(!os_atomic_cmpxchgv2o(dg, dg_state,
		  old_state, new_state, &old_state, relaxed)));
	 return _dispatch_group_wake(dg, old_state, true);
     }
     if (unlikely(old_value == 0)) {
	 DISPATCH_CLIENT_CRASH((uintptr_t)old_value,
		  "Unbalanced call to dispatch_group_leave()");
     }
}
复制代码

通过os_atomic_add_orig2o进行自增(-1 ~ 0)得到old_state = 0

DISPATCH_GROUP_VALUE_MASK    0x00000000fffffffcULL
DISPATCH_GROUP_VALUE_1     DISPATCH_GROUP_VALUE_MASK
复制代码
    1. old_value等于old_state监控 & DISPATCH_GROUP_变量类型有哪些VALUE_MASK等于0,此时old_value != DISPATCH_GROUP_VALUE_1,再由于判断是unlikely,所以会进入if判断
    1. 由于DISPATCH_GROUP_VALUE_INTERVAL = 4,所以此时old_state = 4,再进入do-while循环,此时会走else判断new_state &= ~DISPATCH_GROUP_HAS_NOTIFS= 4 & ~2 = 4,这时old_statenew_state相等,然后跳出循环执行_dispatch_gro变量泵up_wake方法,也就是唤醒dispatch_group_notify方法
    1. 假如enter两次,则old_s监控家用远程手机tate=-1,加上4后3源码编辑器下载,也就是在do-while循环时,new_state = old_state = 3。然后new_state &= ~DISPATCH_GROUP_HAS_NOTIFS = 3 & ~2 = 1,然后不等与old_state会再进行循环,当再次进行leave自增操作后,新的循环判断会走到_dispatch_group_wake函数进行唤醒

dispatch_group_notify

static inline void
_dispatch_group_notify(dispatch_group_t dg, dispatch_queue_t dq,
	 dispatch_continuation_t dsn)
{
     uint64_t old_state, new_state;
     dispatch_continuation_t prev;
     dsn->dc_data = dq;
     _dispatch_retain(dq);
     prev = os_mpsc_push_update_tail(os_mpsc(dg, dg_notify), dsn, do_next);
     if (os_mpsc_push_was_empty(prev)) _dispatch_retain(dg);
     os_mpsc_push_update_prev(os_mpsc(dg, dg_notify), prev, dsn, do_next);
     if (os_mpsc_push_was_empty(prev)) {
	 os_atomic_rmw_loop2o(dg, dg_state, old_state, new_state, release, {
	     new_state = old_state | DISPATCH_GROUP_HAS_NOTIFS;
	     if ((uint32_t)old_state == 0) {
		 os_atomic_rmw_loop_give_up({
		     return _dispatch_group_wake(dg, new_state, false);
		 });
	     }
	 });
     }
}
复制代码

主要是通过os_atomic_r源码之家mw_loop2o进行do-while循环判断,知道线程和进程的区别是什么old_state == 0时就会走_dispatch_group_wake唤醒,也就是会去走block执行

  • 此时还有一个问题没有解决,就是dispatch_group_a源码编辑器sync为什么和进组+出组效果一样,再来分析变量类型有哪些下源码

dispatch_group_async

void
dispatch_group_async(dispatch_group_t dg, dispatch_queue_t dq,
		dispatch_block_t db)
{
    dispatch_continuation_t dc = _dispatch_continuation_alloc();
    uintptr_t dc_flags = DC_FLAG_CONSUME | DC_FLAG_GROUP_ASYNC;
    dispatch_qos_t qos;
    qos = _dispatch_continuation_init(dc, dq, db, 0, dc_flags);
    _dispatch_continuation_group_async(dg, dq, dc, qos);
}

这个函数内容比较熟悉,与异步的函数很像,但此时dc_flags = DC_FLA源码网站G_CONSUME | DC_FLAG_GROUP_ASYNC,然后走进_disp源码精灵永久兑换码atch_continuation_group_async函数:

static inline void
_dispatch_continuation_group_async(dispatch_group_t dg, dispatch_queue_t dq,
		dispatch_continuation_t dc, dispatch_qos_t qos)
{
     dispatch_group_enter(dg);
     dc->dc_data = dg;
     _dispatch_continuation_async(dq, dc, qos, dc->dc_flags);
}

这里我们看到了dispatch_gro源码1688up_enter,但是没有看到leave函数,我猜想肯定在block执行的地方会执行leave,不然不能确保组里任务执行完,于是根据监控global类型的dq_push = _dispatch_root_queue_push最终找到_dispa源码交易平台tch_continuati线程on_invoke_inline函数

iOS多线程GCD(三) 栅栏函数、信号量 、调度组、dispatch_source
如果是普通异步类型会走到_dispatch_client_callout函数,如果是DC_FLAG_GROUP_ASY源码中的图片NC组类型,会走_dispatch线程_continu变量泵ation_with_group_invoke函数

static inline void
_dispatch_continuation_with_group_invoke(dispatch_continuation_t dc)
{
     struct dispatch_object_s *dou = dc->dc_data;
     unsigned long type = dx_type(dou);
     if (type == DISPATCH_GROUP_TYPE) {
	 _dispatch_client_callout(dc->dc_ctxt, dc->dc_func);
	 _dispatch_trace_item_complete(dc);
	 dispatch_group_leave((dispatch_group_t)dou);
     } else {
	 DISPATCH_INTERNAL_CRASH(dx_type(dou), "Unexpected object type");
     }
}
复制代码

此时,当类型是DISPATCH_GROUP_TYPE时,就会先执行_dis源码时代patch_client_callout,然后执行dispatch_group_leave,至此前面的问题全部解决

信号源类型

  • 创建源

    dispatch_source_t
    dispatch_source_create(dispatch_source_type_t type,
        uintptr_t handle,
        uintptr_t mask,
        dispatch_queue_t _Nullable queue);
    复制代码
    
    • 第一个参数是dispatch_source_ty变量之间的关系pe_t类型的type,然后handlemask都是u线程和进程的区别是什么intptr_t类型的,最后要传入一个队列
  • 使用方法:

    dispatch_source_t source = dispatch_source_create(DISPATCH_SOURCE_TYPE_DATA_ADD, 0, 0, dispatch_get_main_queue());
    复制代码
    
  • 源的类型dispatch_source_type_t

      1. DIS监控安装PATCH_SOURCE_TYPE_DATA_ADD:用于ADD合并数据
      1. DISPATCH_SOURCE_TYPE_DATA_OR:用监控摄像头品牌排行按位或合并数据
      1. DISPATCH源码之家_SOURCE_TY源码中的图片PE_DATA_REPLACE跟踪通过调用dispatch_source_merge_data获得的数据的分派源,新获得的数据值线程替换 尚未交付给源处理程序 的现有数据值
      1. DISPATCH_变量名SOURCE_TYPE_MACH_SEND:用于监视Mach端口无效名称通知的调度源,只能发送没有接收权限
      1. DIS源码PATCH_SOURCE_TYPE_MACH_RECV:用于监视Mach端口挂起消息
      1. DISPATCH_SOURCE_TYPE_MEMORYPRESSURE:用于监控系统内存压力变化
      1. DISPATCH_SOURCE_TYPE_PROC:用于监视外部进程的事件
      1. DISPATCH_SOURCE_TYPE_RE变量是什么意思AD监视文件描述符以获取可读取的挂起字节变量的定义的分派源
      1. DISPATCH_SOURCE_TYPE_SIGNAL监控当前进程以获取信号的调度源
      1. DISPATCH_SOURCE_TYPE_TIMER:基于计时器提交事件处理程序块的分派源
      1. DISPA监控安装TCH_SOURCE_TYPE_VNODE:用于监视文件描述符中定义的事件监控安装的分派源
      1. DISPATCH_SOURCE_TYPE_WRIT变量是什么意思E监视文件描述符以获取可写入字节的可用缓冲区空间的分派源。

定时器

下面使用Dispatch Source来封装一个定时器源码编辑器

- (void)testTimer {
    self.timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_global_queue(0, 0));
    dispatch_time_t startTime = dispatch_time(DISPATCH_TIME_NOW, 0);
    dispatch_source_set_timer(self.timer, startTime, 1 * NSEC_PER_SEC, 0);
    __block int a = 0;
    dispatch_source_set_event_handler(self.timer, ^{
        a++;
        NSLog(@"a 的 值 %d", a);
    });
    dispatch_resume(self.timer);
    self.isRunning = YES;
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    if (self.isRunning) {
        dispatch_suspend(self.timer);
//        dispatch_source_cancel(self.timer);
        self.isRunning = NO;
        NSLog(@" 中场休息下~ ");
    } else {
        dispatch_resume(self.timer);
        self.isRunning = YES;
        NSLog(@" 继续喝~ ");
    }
}
  • 创建定时器dispatch_source_create时,一定要用属性或者实例变量接收,不然定时线程数是什么器不会执行

  • dispatch_source_set_timer的第二个参数start是从什么时候开始,第三个参数in变量是什么意思terval是时间间隔,leeway是计时器的纳秒偏差

  • 计时器停止有两种:

    • dispatch_resume:计时器暂停但一直在线,可以唤醒
    • dispatch_so监控家用远程手机urce_can源码网站cel:计时器释放,执行dispatch_线程池resume唤醒,线程会崩溃

发表评论

提供最优质的资源集合

立即查看 了解详情