zoukankan      html  css  js  c++  java
  • 「面向打野编程」iOS多线程:CGD

    「面向打野编程」iOS多线程:CGD

    前言

    参考网络其他文章而写,渣水平,抛砖引玉。

    虽然Concurrent意思为并发,但由于队列的实际效果,以下称为并行队列。

    当前iPhone的CPU核心数远小于GCD的线程池,故不讨论GCD的线程池,没有意义。

    GCD = 主队列 + 并行队列 * n

    异步串行队列 = 并行队列 * 1

    1. 同异步队列

    • 主队列dispatch_get_main_queue()

    • 全局并行队列dispatch_get_global_queue(0, 0)

    • 串行队列dispatch_queue_t sQue = dispatch_queue_create("q1", DISPATCH_QUEUE_SERIAL);

    • 并行队列dispatch_queue_t cQue = dispatch_queue_create("q2", DISPATCH_QUEUE_CONCURRENT);

    GCD串行队列并行队列
    同步执行 抢占执行 抢占执行
    异步执行 等待执行 并行执行
    • 同步 + 队列 = 主队列
    • (异步 + 并行) * n = 串行队列 * n
    • 串行(同步 + 自身) = 死锁(无法抢占)

    同步执行:抢占当前主线程,不能在主线程中使用,会造成死锁。

    异步执行:将任务加入队列末尾,或将任务加入新线程。


    主队列并行队列互不影响,即使并行队列被长耗时任务占满,主队列依然畅通。

    经过测试,全局并行队列与自定义并行队列并没有太多明显的区别。当全局并行队列占满核心后,自定义并行队列依然会陷入等待,无法并发。

    dispatch_queue_t cQue = dispatch_queue_create("cq", DISPATCH_QUEUE_CONCURRENT);
    for(int i = 0; i < 20; ++i) {
        dispatch_async(dispatch_get_global_queue(0, 0), ^{
            for(int j = 1; j < INT_MAX; ++j);
            for(int j = 1; j < INT_MAX; ++j);
            printf("_");
        });
    }
    for(int i = 0; i < 20; ++i) {
        dispatch_async(cQue, ^{
            printf("2");
        });
    }
    ________________22222222222222222222____

    CPU的核心数量是有限的,当异步线程的数量主够多的时候,会退化为串行队列,并不会变为并发执行。对于多个长时间运行的并行线程,可能会因为占满处理器而导致其他线程任务的堆积,可以使用[NSThread sleepForTimeInterval:(NSTimeInterval)]手动分割控制阻塞实现伪并发操作来改善长时间等待。

    [NSThread sleepForTimeInterval:(NSTimeInterval)]

    dispatch_queue_t cQue = dispatch_queue_create("cq", DISPATCH_QUEUE_CONCURRENT);
    for(int i = 0; i < 20; ++i) {
        dispatch_async(dispatch_get_global_queue(0, 0), ^{
            for(int j = 1; j < INT_MAX>>1; ++j);
            [NSThread sleepForTimeInterval:2];
            for(int j = 1; j < INT_MAX>>1; ++j);
            [NSThread sleepForTimeInterval:1];
            for(int j = 1; j < INT_MAX>>1; ++j);
            [NSThread sleepForTimeInterval:0.5];
            for(int j = 1; j < INT_MAX>>1; ++j);
            printf("_");
        });
    }
    for(int i = 0; i < 20; ++i) {
        dispatch_async(cQue, ^{
            printf("2");
        });
    }
    _____22222222222222222222_______________

    手动分割

    dispatch_queue_t cQue = dispatch_queue_create("cq", DISPATCH_QUEUE_CONCURRENT);
    for(int i = 0; i < 20; ++i) {
        dispatch_async(dispatch_get_global_queue(0, 0), ^{
            for(int j = 1; j < INT_MAX>>1; ++j);
            dispatch_async(dispatch_get_global_queue(0, 0), ^{
                for(int j = 1; j < INT_MAX>>1; ++j);
                dispatch_async(dispatch_get_global_queue(0, 0), ^{
                    for(int j = 1; j < INT_MAX>>1; ++j);
                    dispatch_async(dispatch_get_global_queue(0, 0), ^{
                        for(int j = 1; j < INT_MAX>>1; ++j);
                        printf("_");
                    });
                });
            });
        });
    }
    for(int i = 0; i < 20; ++i) {
        dispatch_async(cQue, ^{
            printf("2");
        });
    }
    22222222222222222222____________________

    2. 栅栏

    • 同步栅栏dispatch_barrier_async

    • 异步栅栏dispatch_barrier_async

    栅栏对全局并行队列无效,只能操作自定义并行队列。想要运行此处的代码就需要完成先前的操作,是否同步的区别在于能否阻塞当前线程。千万不要对把同步栅栏运行在同个队列,会造成死锁。


    同步栅栏

    dispatch_queue_t cQue = dispatch_queue_create("q", DISPATCH_QUEUE_CONCURRENT);
    dispatch_async(cQue, ^{
        for(int j = 1; j < INT_MAX; ++j);
        puts("1");
    });
    dispatch_async(cQue, ^{
        for(int j = 1; j < INT_MAX; ++j);
        puts("2");
    });
    dispatch_async(cQue, ^{
        for(int j = 1; j < INT_MAX; ++j);
        puts("3");
    });
    dispatch_barrier_sync(cQue, ^{
        NSLog(@"4");
    });
    puts("-----5-------");
    3
    2
    1
    4
    -----5-------

    异步栅栏

    dispatch_queue_t cQue = dispatch_queue_create("q", DISPATCH_QUEUE_CONCURRENT);
    dispatch_async(cQue, ^{
        for(int j = 1; j < INT_MAX; ++j);
        puts("1");
    });
    dispatch_async(cQue, ^{
        for(int j = 1; j < INT_MAX; ++j);
        puts("2");
    });
    dispatch_async(cQue, ^{
        for(int j = 1; j < INT_MAX; ++j);
        puts("3");
    });
    dispatch_barrier_async(cQue, ^{
        NSLog(@"4");
    });
    puts("-----5-------");
    -----5-------
    1
    2
    3
    4

    死锁

    dispatch_queue_t cQue2 = dispatch_queue_create("q22", DISPATCH_QUEUE_CONCURRENT);
    dispatch_async(cQue2, ^{
        dispatch_async(cQue2, ^{
            puts("1");
        });
        dispatch_barrier_sync(cQue2, ^{
            puts("----------");
        });
        dispatch_async(cQue2, ^{
            puts("2");
        });
    });
    1

    3. 延迟执行

    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        //延迟0.5s执行
    });

    4. 一次性代码

    只执行一次,但是需要注意不要出现相互调用,会出现死锁

    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        //code to be executed once
    });

    5. 快速遍历

    不用手写循环,或同时执行多个相似的代码块,会阻塞当前线程。

    dispatch_apply(6, dispatch_get_global_queue(0, 0), ^(size_t i) {
        NSLog(@"%zu", i);
    });
    2017-12-15 11:34:56.748284+0800 GCD-OC[2266:105786] 1
    2017-12-15 11:34:56.748260+0800 GCD-OC[2266:105750] 0
    2017-12-15 11:34:56.748294+0800 GCD-OC[2266:105785] 2
    2017-12-15 11:34:56.748302+0800 GCD-OC[2266:105788] 3
    2017-12-15 11:34:56.749694+0800 GCD-OC[2266:105750] 4
    2017-12-15 11:34:56.749703+0800 GCD-OC[2266:105786] 5

    6. 信号量

    控制最大并行数,或用于同步,然而容易被其他方法取代。

    • 创建信号量dispatch_semaphore_create(long value)

    相当于并行数,运行同时执行的线程数。

    • 获取信号量dispatch_semaphore_wait(dispatch_semaphore_t _Nonnull dsema, dispatch_time_t timeout)

    如果信号量,返回0表示获取成功,否则阻塞主线程开始等待,超过等待时间后继续执行代码。

    • 增加信号量dispatch_semaphore_signal(dispatch_semaphore_t _Nonnull dsema)

    信号量+1。

    dispatch_semaphore_t semaphore = dispatch_semaphore_create(1);
    long a = dispatch_semaphore_wait(semaphore, DISPATCH_TIME_NOW);
    puts(a ? "1" : "0");
    a = dispatch_semaphore_wait(semaphore, DISPATCH_TIME_NOW);
    puts(a ? "1" : "0");
    dispatch_semaphore_signal(semaphore);
    for(int i = 0; i < 6; ++i) {
        dispatch_async(dispatch_get_global_queue(0, 0), ^{
            long a = dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
            sleep(3);
            NSLog(a ? @"YES" : @"NO");
            dispatch_semaphore_signal(semaphore);
        });
    }
    puts("----------");
    0
    1
    ----------
    2017-12-15 15:55:11.589061+0800 GCD-OC[4209:250818] NO
    2017-12-15 15:55:14.593238+0800 GCD-OC[4209:250816] NO
    2017-12-15 15:55:17.594262+0800 GCD-OC[4209:250817] NO
    2017-12-15 15:55:20.597262+0800 GCD-OC[4209:250815] NO
    2017-12-15 15:55:23.599650+0800 GCD-OC[4209:250850] NO
    2017-12-15 15:55:26.603327+0800 GCD-OC[4209:250851] NO

    7. dispatch_group

    先完成前面的任务,再执行最后一个任务。相当于异步栅栏,但可以将任务给予任意队列。

    创建groupdispatch_group_t group = dispatch_group_create();

    添加任务

    dispatch_group_async(dispatch_group_t  _Nonnull group, dispatch_queue_t  _Nonnull queue, ^{
        code
    });

    最终任务

    dispatch_group_notify(dispatch_group_t  _Nonnull group, dispatch_queue_t  _Nonnull queue, ^{
        code
    });

    dispatch_queue_t cQue1 = dispatch_queue_create("q11", DISPATCH_QUEUE_CONCURRENT);
    dispatch_queue_t cQue2 = dispatch_queue_create("q22", DISPATCH_QUEUE_CONCURRENT);
    dispatch_group_t group = dispatch_group_create();
    dispatch_group_async(group, cQue1, ^(){
        sleep(2);
        puts("1");
    });
    dispatch_group_async(group, cQue2, ^(){
        sleep(1);
        puts("2");
    });
    dispatch_group_notify(group, dispatch_get_main_queue(), ^(){
        puts("3");
    });
    puts("-------");
    -------
    2
    1
    3
  • 相关阅读:
    php命令注入
    mysql事物
    安装php环境
    移除服务器缓存实例
    show user profile synchronization tools
    manual start user profile import
    JSON is undefined. Infopath Form People Picker in SharePoint 2013
    asp.net web 应用站点支持域账户登录
    Load sharepoint envirement by powershell
    sharepoint 2016 download
  • 原文地址:https://www.cnblogs.com/Simon-X/p/8072513.html
Copyright © 2011-2022 走看看