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
  • 相关阅读:
    OCP-1Z0-053-V12.02-15题
    Boost.Asio性能测试
    使用asio搭建服务器
    boost::asio::ip::tcp::socket is connected?(如何知道socket的链接是链接或断开?)
    boost::async_read_some连续接收数据
    基于boost asio实现的支持ssl的通用socket框架
    Boost.Asio的使用技巧
    Matlab基本函数-expm函数
    Matlab基本函数-exp函数
    OCP-1Z0-053-V12.02-337题
  • 原文地址:https://www.cnblogs.com/Simon-X/p/8072513.html
Copyright © 2011-2022 走看看