YYKit-YYDispatchQueuePool分析

1. YYDispatchQueuePool是什么?

YYDispatchQueuePool是一个线程/队列池。

2. YYDispatchQueuePool的作用?

充分利用时间片和多核的效率,同时不出现相关副作用的情况下实现多线程操作。

3. YYDispatchQueuePool如何实现?

YYDispatchQueuePool只暴露了defaultPoolForQOS来创建一个单例的线程池

+ (instancetype)defaultPoolForQOS:(NSQualityOfService)qos {
    switch (qos) {
        case NSQualityOfServiceUserInteractive: {
            static YYDispatchQueuePool *pool;
            static dispatch_once_t onceToken;
            dispatch_once(&onceToken, ^{
                pool = [[YYDispatchQueuePool alloc] initWithContext:YYDispatchContextGetForQOS(qos)];
            });
            return pool;
        } break;
        case NSQualityOfServiceUserInitiated: {
            static YYDispatchQueuePool *pool;
            static dispatch_once_t onceToken;
            dispatch_once(&onceToken, ^{
                pool = [[YYDispatchQueuePool alloc] initWithContext:YYDispatchContextGetForQOS(qos)];
            });
            return pool;
        } break;
        case NSQualityOfServiceUtility: {
            static YYDispatchQueuePool *pool;
            static dispatch_once_t onceToken;
            dispatch_once(&onceToken, ^{
                pool = [[YYDispatchQueuePool alloc] initWithContext:YYDispatchContextGetForQOS(qos)];
            });
            return pool;
        } break;
        case NSQualityOfServiceBackground: {
            static YYDispatchQueuePool *pool;
            static dispatch_once_t onceToken;
            dispatch_once(&onceToken, ^{
                pool = [[YYDispatchQueuePool alloc] initWithContext:YYDispatchContextGetForQOS(qos)];
            });
            return pool;
        } break;
        case NSQualityOfServiceDefault:
        default: {
            static YYDispatchQueuePool *pool;
            static dispatch_once_t onceToken;
            dispatch_once(&onceToken, ^{
                pool = [[YYDispatchQueuePool alloc] initWithContext:YYDispatchContextGetForQOS(NSQualityOfServiceDefault)];
            });
            return pool;
        } break;
    }
}

我们可以看下具体的线程池是如何创建的:

- (instancetype)initWithName:(NSString *)name queueCount:(NSUInteger)queueCount qos:(NSQualityOfService)qos {
    if (queueCount == 0 || queueCount > MAX_QUEUE_COUNT) return nil;
    self = [super init];

    //创建一个包含队列数组的context
    _context = YYDispatchContextCreate(name.UTF8String, (uint32_t)queueCount, qos);
    if (!_context) return nil;
    _name = name;
    return self;
}

我们看下这个上下文:

typedef struct {
    const char *name; //上下文环境名称
    void **queues;  //队列数组指针
    uint32_t queueCount; //同步队列的个数
    int32_t counter; //计数器
} YYDispatchContext;

我们看下YYDispatchContextCreate是如何创建上下文的:

static YYDispatchContext *YYDispatchContextCreate(const char *name,
                                                 uint32_t queueCount,
                                                 NSQualityOfService qos) {

    //创建一块内存空间
    YYDispatchContext *context = calloc(1, sizeof(YYDispatchContext));
    if (!context) return NULL;

    //创建队列数组指针的内存空间
    context->queues =  calloc(queueCount, sizeof(void *));
    if (!context->queues) {
        free(context);
        return NULL;
    }
    if ([UIDevice currentDevice].systemVersion.floatValue >= 8.0) {

        //根据qos返回对应级别的优先级
        dispatch_qos_class_t qosClass = NSQualityOfServiceToQOSClass(qos);
        for (NSUInteger i = 0; i < queueCount; i++) {
            dispatch_queue_attr_t attr = dispatch_queue_attr_make_with_qos_class(DISPATCH_QUEUE_SERIAL, qosClass, 0);
            dispatch_queue_t queue = dispatch_queue_create(name, attr);

            //将Object-C对象转化为Core Foundation对象,同时让队列持有该对象
            context->queues[i] = (__bridge_retained void *)(queue);
        }
    } else {
        long identifier = NSQualityOfServiceToDispatchPriority(qos);
        for (NSUInteger i = 0; i < queueCount; i++) {
            dispatch_queue_t queue = dispatch_queue_create(name, DISPATCH_QUEUE_SERIAL);
            dispatch_set_target_queue(queue, dispatch_get_global_queue(identifier, 0));
            context->queues[i] = (__bridge_retained void *)(queue);
        }
    }
    context->queueCount = queueCount;
    if (name) {

         //使用strdup创新新地址装载name,防止name修改的影响
         context->name = strdup(name);
    }
    return context;
}

队列中的优先级是一个枚举类型表示的:

__QOS_ENUM(qos_class, unsigned int,
    QOS_CLASS_USER_INTERACTIVE -----表示用户需要立即执行提供好的体验(不耗时操作),用来更新UI,响应事件等。这种优先级最好保持小规模。
            __QOS_CLASS_AVAILABLE(macos(10.10), ios(8.0)) = 0x21,
    QOS_CLASS_USER_INITIATED ------表示任务由用户发起异步执行(不耗时)。适用于需要及时得到结果同时又可以继续交互的场景。 
            __QOS_CLASS_AVAILABLE(macos(10.10), ios(8.0)) = 0x19,
    QOS_CLASS_DEFAULT 
            __QOS_CLASS_AVAILABLE(macos(10.10), ios(8.0)) = 0x15,
    QOS_CLASS_UTILITY ----表示需要长时间运行的任务,盼有用户可见进度指示器。经常会用于做计算、i/o、网络、持续的数据填充等任务。
            __QOS_CLASS_AVAILABLE(macos(10.10), ios(8.0)) = 0x11,
    QOS_CLASS_BACKGROUND -----表示用户不会察觉的任务,使用来处理预加载,或者不需要用户交互和对事件不敏感的任务。
            __QOS_CLASS_AVAILABLE(macos(10.10), ios(8.0)) = 0x09,
    QOS_CLASS_UNSPECIFIED
            __QOS_CLASS_AVAILABLE(macos(10.10), ios(8.0)) = 0x00,
);------IOS8之后的优先级设置

对于Object-C用户,我们使用NSQualityOfService:

typedef NS_ENUM(NSInteger, NSQualityOfService) {
    /* UserInteractive QoS is used for work directly involved in providing an interactive UI such as processing events or drawing to the screen. */
    NSQualityOfServiceUserInteractive = 0x21,

    /* UserInitiated QoS is used for performing work that has been explicitly requested by the user and for which results must be immediately presented in order to allow for further user interaction.  For example, loading an email after a user has selected it in a message list. */
    NSQualityOfServiceUserInitiated = 0x19,

    /* Utility QoS is used for performing work which the user is unlikely to be immediately waiting for the results.  This work may have been requested by the user or initiated automatically, does not prevent the user from further interaction, often operates at user-visible timescales and may have its progress indicated to the user by a non-modal progress indicator.  This work will run in an energy-efficient manner, in deference to higher QoS work when resources are constrained.  For example, periodic content updates or bulk file operations such as media import. */
    NSQualityOfServiceUtility = 0x11,

    /* Background QoS is used for work that is not user initiated or visible.  In general, a user is unaware that this work is even happening and it will run in the most efficient manner while giving the most deference to higher QoS work.  For example, pre-fetching content, search indexing, backups, and syncing of data with external systems. */
    NSQualityOfServiceBackground = 0x09,

    /* Default QoS indicates the absence of QoS information.  Whenever possible QoS information will be inferred from other sources.  If such inference is not possible, a QoS between UserInitiated and Utility will be used. */
    NSQualityOfServiceDefault = -1
} API_AVAILABLE(macos(10.10), ios(8.0), watchos(2.0), tvos(9.0));

YYDispatchQueuePool提供了两者的转化:

static inline qos_class_t NSQualityOfServiceToQOSClass(NSQualityOfService qos) {
    switch (qos) {
        case NSQualityOfServiceUserInteractive: return QOS_CLASS_USER_INTERACTIVE;
        case NSQualityOfServiceUserInitiated: return QOS_CLASS_USER_INITIATED;
        case NSQualityOfServiceUtility: return QOS_CLASS_UTILITY;
        case NSQualityOfServiceBackground: return QOS_CLASS_BACKGROUND;
        case NSQualityOfServiceDefault: return QOS_CLASS_DEFAULT;
        default: return QOS_CLASS_UNSPECIFIED;
    }
}

对于Object-C与Core Foundation对象之间的转换,在ARC的机制下,需要使用__bridge 进行类型转化:

id obj = [[NSObject alloc] init];
void *p = (__bridge void *)obj;
id o = (__bridge id)p;

而直接使用__bridge转化类型的安全性与__bridge_unretain相差无几,甚至更低。因为赋值对象一旦被释放(obj被释放),此时被赋值的对象还在操作的话会导致崩溃(p)。
我们可以使用__bridge_retain/__bridge_transfer来进行类型转化,让被赋值对象持有赋值对象。而__bridge_retain是被赋值对象retain,__bridge_transfer是被赋值对象retain,同时赋值对象release操作:

__bridge_retain等同于:

id obj = [[NSObject alloc] init];
void *p = obj;
[(id)p retain];

__bridge_transfer等同于:

id obj = (id)p;
[obj retain];
[(id)p release];

当队列池创建好后,我们可以从队列池中获取到队列进行异步操作:

- (dispatch_queue_t)queue {
    return YYDispatchContextGetQueue(_context);
}
static dispatch_queue_t YYDispatchContextGetQueue(YYDispatchContext *context) {

    //ios10之后OSAtomicIncrement32已经被摒弃,使用atomic_fetch_add_explicit来替代。原子操作,保证@content->counter计数器的线程安全
    uint32_t counter = (uint32_t)OSAtomicIncrement32(&context->counter);

    //从队列数组中取出队列(轮询的方式取出队列)
    void *queue = context->queues[counter % context->queueCount];
    return (__bridge dispatch_queue_t)(queue);
}

针对于使用- (instancetype)initWithName:(NSString *)name queueCount:(NSUInteger)queueCount qos:(NSQualityOfService)qos创建的pool对象,在pool被释放的时候调用以下函数进行释放:

static void YYDispatchContextRelease(YYDispatchContext *context) {
    if (!context) return;
    if (context->queues) {
        for (NSUInteger i = 0; i < context->queueCount; i++) {
            void *queuePointer = context->queues[i];
            dispatch_queue_t queue = (__bridge_transfer dispatch_queue_t)(queuePointer);
            const char *name = dispatch_queue_get_label(queue);
            if (name) strlen(name);
            queue = nil;
        }
        free(context->queues);
        context->queues = NULL;
    }
    if (context->name) free((void *)context->name);
    free(context);
}
4. YYDispatchQueuePool为什么这样设计?
YYDispatchQueuePool可以使用三种形式来获取使用:
1. 调用YYDispatchQueuePool的初始化方法:
- (instancetype)initWithName:(NSString *)name queueCount:(NSUInteger)queueCount qos:(NSQualityOfService)qos {
    if (queueCount == 0 || queueCount > MAX_QUEUE_COUNT) return nil;
    self = [super init];
    _context = YYDispatchContextCreate(name.UTF8String, (uint32_t)queueCount, qos);
    if (!_context) return nil;
    _name = name;
    return self;
}

然后调用YYDispatchQueuePool的实例方法queue轮询队列数组中的队列进行异步操作:

- (dispatch_queue_t)queue {
    return YYDispatchContextGetQueue(_context);
}

static dispatch_queue_t YYDispatchContextGetQueue(YYDispatchContext *context) {
    uint32_t counter = (uint32_t)OSAtomicIncrement32(&context->counter);
    void *queue = context->queues[counter % context->queueCount];
    return (__bridge dispatch_queue_t)(queue);
}

当结束调用的时候对队列进行释放操作:

- (void)dealloc {
    if (_context) {
        YYDispatchContextRelease(_context);
        _context = NULL;
    }
}

static void YYDispatchContextRelease(YYDispatchContext *context) {
    if (!context) return;
    if (context->queues) {
        for (NSUInteger i = 0; i < context->queueCount; i++) {
            void *queuePointer = context->queues[i];
            dispatch_queue_t queue = (__bridge_transfer dispatch_queue_t)(queuePointer);
            const char *name = dispatch_queue_get_label(queue);
            if (name) strlen(name);
            queue = nil;
        }
        free(context->queues);
        context->queues = NULL;
    }
    if (context->name) free((void *)context->name);
    free(context);
}
2. 使用YYDispatchQueuePool的类方法:
+ (instancetype)defaultPoolForQOS:(NSQualityOfService)qos {
    switch (qos) {
        case NSQualityOfServiceUserInteractive: {
            static YYDispatchQueuePool *pool;
            static dispatch_once_t onceToken;
            dispatch_once(&onceToken, ^{
                pool = [[YYDispatchQueuePool alloc] initWithContext:YYDispatchContextGetForQOS(qos)];
            });
            return pool;
        } break;
        case NSQualityOfServiceUserInitiated: {
            static YYDispatchQueuePool *pool;
            static dispatch_once_t onceToken;
            dispatch_once(&onceToken, ^{
                pool = [[YYDispatchQueuePool alloc] initWithContext:YYDispatchContextGetForQOS(qos)];
            });
            return pool;
        } break;
        case NSQualityOfServiceUtility: {
            static YYDispatchQueuePool *pool;
            static dispatch_once_t onceToken;
            dispatch_once(&onceToken, ^{
                pool = [[YYDispatchQueuePool alloc] initWithContext:YYDispatchContextGetForQOS(qos)];
            });
            return pool;
        } break;
        case NSQualityOfServiceBackground: {
            static YYDispatchQueuePool *pool;
            static dispatch_once_t onceToken;
            dispatch_once(&onceToken, ^{
                pool = [[YYDispatchQueuePool alloc] initWithContext:YYDispatchContextGetForQOS(qos)];
            });
            return pool;
        } break;
        case NSQualityOfServiceDefault:
        default: {
            static YYDispatchQueuePool *pool;
            static dispatch_once_t onceToken;
            dispatch_once(&onceToken, ^{
                pool = [[YYDispatchQueuePool alloc] initWithContext:YYDispatchContextGetForQOS(NSQualityOfServiceDefault)];
            });
            return pool;
        } break;
    }
}

返回单例对象,适用于应用的整个生命周期

3. 调用全局的获取方法:
dispatch_queue_t YYDispatchQueueGetForQOS(NSQualityOfService qos) {
    return YYDispatchContextGetQueue(YYDispatchContextGetForQOS(qos));
}

static YYDispatchContext *YYDispatchContextGetForQOS(NSQualityOfService qos) {
    static YYDispatchContext *context[5] = {0};
    switch (qos) {
        case NSQualityOfServiceUserInteractive: {
            static dispatch_once_t onceToken;
            dispatch_once(&onceToken, ^{
                int count = (int)[NSProcessInfo processInfo].activeProcessorCount;
                count = count < 1 ? 1 : count > MAX_QUEUE_COUNT ? MAX_QUEUE_COUNT : count;
                context[0] = YYDispatchContextCreate("com.ibireme.yykit.user-interactive", count, qos);
            });
            return context[0];
        } break;
        case NSQualityOfServiceUserInitiated: {
            static dispatch_once_t onceToken;
            dispatch_once(&onceToken, ^{
                int count = (int)[NSProcessInfo processInfo].activeProcessorCount;
                count = count < 1 ? 1 : count > MAX_QUEUE_COUNT ? MAX_QUEUE_COUNT : count;
                context[1] = YYDispatchContextCreate("com.ibireme.yykit.user-initiated", count, qos);
            });
            return context[1];
        } break;
        case NSQualityOfServiceUtility: {
            static dispatch_once_t onceToken;
            dispatch_once(&onceToken, ^{
                int count = (int)[NSProcessInfo processInfo].activeProcessorCount;
                count = count < 1 ? 1 : count > MAX_QUEUE_COUNT ? MAX_QUEUE_COUNT : count;
                context[2] = YYDispatchContextCreate("com.ibireme.yykit.utility", count, qos);
            });
            return context[2];
        } break;
        case NSQualityOfServiceBackground: {
            static dispatch_once_t onceToken;
            dispatch_once(&onceToken, ^{
                int count = (int)[NSProcessInfo processInfo].activeProcessorCount;
                count = count < 1 ? 1 : count > MAX_QUEUE_COUNT ? MAX_QUEUE_COUNT : count;
                context[3] = YYDispatchContextCreate("com.ibireme.yykit.background", count, qos);
            });
            return context[3];
        } break;
        case NSQualityOfServiceDefault:
        default: {
            static dispatch_once_t onceToken;
            dispatch_once(&onceToken, ^{
                int count = (int)[NSProcessInfo processInfo].activeProcessorCount;
                count = count < 1 ? 1 : count > MAX_QUEUE_COUNT ? MAX_QUEUE_COUNT : count;
                context[4] = YYDispatchContextCreate("com.ibireme.yykit.default", count, qos);
            });
            return context[4];
        } break;
    }
}

全局的方法返回的是全局上下文数组,也是在应用的整个生命周期存在。

由于存在以上三种方式操作YYDispatchQueuePool,所以存在局部的YYDispatchQueuePool对象、全局的YYDispatchQueuePool对象、全局的Context[5]数组。

为什么要使用队列数组(每个队列是同步队列)来实现队列池呢?

其一,创建系统激活的核数大小的队列数组,是为了充分利用多核的效率,同时也尽可能的减少时间片的切换带来的cpu消耗的影响。

其二,在dispatch_async中使用队列,意味这创建了对应这么多的异步线程,同时每个线程中队列是同步队列,保证了不会创建更多的线程,同时任务在异步操作中每个任务间是同步操作的。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
iOS 中的 UILabel 是一种用于显示文本的控件,它可以用于显示降价信息。你可以使用具有 Markdown 格式的 UILabel,例如使用 YYText 组件。YYText 是 YYKit 的一个组件,它是一个功能强大的 iOS 文本框架,用于显示和编辑富文本。它具有高性能的异步文本布局和渲染,并且扩展了 CoreText 的属性,提供了更多的文本效果。你可以使用 NSMutableAttributedString 来创建富文本字符串,并将其设置给 UILabel 的 attributedText 属性,以实现降价信息的显示。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [CDMarkdownKit:广泛的Swift框架,提供简单且可自定义的markdown解析](https://download.csdn.net/download/weixin_42099087/15035498)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT3_1"}}] [.reference_item style="max-width: 33.333333333333336%"] - *2* [YYText:用于显示和编辑富文本的 iOS 文本框架-开源](https://download.csdn.net/download/weixin_42116672/20385582)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT3_1"}}] [.reference_item style="max-width: 33.333333333333336%"] - *3* [GitHub - ElvisCen/PPCounter: 一款简单实用的数字加减动画,支持UILabel、UIButton显示](https://blog.csdn.net/weixin_36159799/article/details/117478907)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT3_1"}}] [.reference_item style="max-width: 33.333333333333336%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值