iOS多线程 -- GCD相关学习笔记

iOS开发中使用最多的是GCD, 基础内容参考官方文档. 开发中我们有几个概念需要了解:

最重要的两个概念: 任务(task)队列(queue).

在学习GCD之前, 最重要的是理解 任务队列.

GCD Queue类型

queue: 是GCD的调度核心, 所有的任务都是同步异步加入到queue中, 多个任务被添加到queue以后, 任务的执行顺序, 由queue类型决定.

queue有以下类型:

  • serial queue: 串行队列.追加处理必须按照顺序一个个执行.并且 1.等待现在执行中处理结束, 2.使用一个线程

  • concurrent queue: 并行队列.可以并行执行多个追加处理.并且 1.不等待现在执行中处理结束, 2.使用多个线程

  • main queue: 主队列. 是一个全局唯一的 serial queue, 它只会将任务调度到main thread中执行.

多个serial dispatch queue 可以并行执行.

GCD 同步与异步执行

异步执行与同步执行:

  1. 异步执行(调度): 就是将指定的block, 非同步地追加到指定的Dispatch Queue中. dispatch_async函数不做任何等待.
  2. 同步执行(调度): 就是将指定的block, 同步地追加到指定的Dispatch Queue中. dispatch_async函数会一直等待处理执行结束.

等待 意味着当前线程会停止.

同步与异步调用: dispatch_asyncdispatch_sync两者区别是:

  1. 是否会创建一个新thread去执行task
  2. 调用方法是否会先返回

并行队列与串行队列的特点(都是在异步(dispatch_async)函数下有效):

  1. 并行队列: 队列中的任务可以同时执行.会开启多个线程, 让任务同时执行.
  2. 串行队列: 队列中的任务必须顺序执行.每次只执行一个任务,一个任务执行完成以后, 接着执行下一个任务.

注意:

  1. 并行队列的并发功能只有在异步(dispatch_async)函数下才有效,如果在同步(dispatch_sync)函数下, 执行只在当前线程顺序执行.
  2. 串行队列异步执行(dispatch_async)执行, 会开启一个新线程, 任务顺序执行.同步(dispatch_sync)函数下执行, 可能造成死锁.

如果组合有以下特点:

iOS多线程 -- GCD相关学习笔记_第1张图片

具体的调用方式与队列类型组合实例

1. 异步调用 + 并行队列

/*
  主线程 + 异步执行 + 并行队列

  1. ---start---, thread: {number = 1, name = main}
  2. ---end---, thread: {number = 1, name = main}
  3. 任务3---thread:{number = 3, name = (null)}---queue:
  4. 任务1---thread:{number = 4, name = (null)}---queue:
  5. 任务2---thread:{number = 5, name = (null)}---queue:
 */
- (void)asyncConcurrent1{
    //创建一个并行队列
    dispatch_queue_t queue = dispatch_queue_create("my queue", DISPATCH_QUEUE_CONCURRENT);
    NSLog(@"---start---, thread: %@", [NSThread currentThread]);
    //使用异步函数调用会创建新线程, 并行队列会创建多个线程.
    dispatch_async(queue, ^{
        NSLog(@"任务1---thread:%@---queue:%@", [NSThread currentThread], queue);
    });
    dispatch_async(queue, ^{
        NSLog(@"任务2---thread:%@---queue:%@", [NSThread currentThread], queue);
    });
    dispatch_async(queue, ^{
        NSLog(@"任务3---thread:%@---queue:%@", [NSThread currentThread], queue);
    });
    NSLog(@"---end---, thread: %@", [NSThread currentThread]);
}


/*
 子线程 + 异步执行 + 并行队列
 
 1. ---start---, thread: {number = 3, name = (null)}
 2. ---end---, thread: {number = 3, name = (null)}
 3. 任务1---thread:{number = 4, name = (null)}---queue:
 4. 任务2---thread:{number = 5, name = (null)}---queue:
 5. 任务3---thread:{number = 6, name = (null)}---queue:
 */
- (void)asyncConcurrent2 {
    dispatch_queue_t queue = dispatch_queue_create("my queue", DISPATCH_QUEUE_CONCURRENT);
    [NSThread detachNewThreadWithBlock:^{
        NSLog(@"---start---, thread: %@", [NSThread currentThread]);
        // 异步调用,  会为queue开启线程, concurrent, 会开启多个线程
        dispatch_async(queue, ^{
            NSLog(@"任务1---thread:%@---queue:%@", [NSThread currentThread], queue);
        });
        dispatch_async(queue, ^{
            NSLog(@"任务2---thread:%@---queue:%@", [NSThread currentThread], queue);
        });
        dispatch_async(queue, ^{
            NSLog(@"任务3---thread:%@---queue:%@", [NSThread currentThread], queue);
        });
        NSLog(@"---end---, thread: %@", [NSThread currentThread]);
    }];
}

结果解释:

  1. 先打印start, 然后打印end. async调用不会阻塞当前线程, 会跳过task, 继续向后执行.
  2. async调用, 会创建线程. 这里创建了3个线程.
  3. 执行 task1, task2, task3, 三者都在不同线程中执行, 并且是并行执行.
iOS多线程 -- GCD相关学习笔记_第2张图片
异步调用 + 并行队列流程

2. 异步执行 + 串行队列

/*
 主线程 + 异步执行 + 串行队列
 1. ---start---, thread: {number = 1, name = main}
 2. ---end---, thread: {number = 1, name = main}
 3. 任务1---thread:{number = 3, name = (null)}---queue:
 4. 任务2---thread:{number = 3, name = (null)}---queue:
 5. 任务3---thread:{number = 3, name = (null)}---queue:
 */
- (void)asyncSerial1{
    //创建一个串行队列
    dispatch_queue_t queue = dispatch_queue_create("my queue2", DISPATCH_QUEUE_SERIAL);
    NSLog(@"---start---, thread: %@", [NSThread currentThread]);

    // 异步调用, 会开启新线程,串行队列只会开启一个线程
    dispatch_async(queue, ^{
        NSLog(@"任务1---thread:%@---queue:%@", [NSThread currentThread], queue);
    });
    dispatch_async(queue, ^{
        NSLog(@"任务2---thread:%@---queue:%@", [NSThread currentThread], queue);
    });
    dispatch_async(queue, ^{
        NSLog(@"任务3---thread:%@---queue:%@", [NSThread currentThread], queue);
    });
    NSLog(@"---end---, thread: %@", [NSThread currentThread]);
} 

/*
 子线程 + 异步执行 + 串行队列
 1. ---start---, thread: {number = 3, name = (null)}
 2. ---end---, thread: {number = 3, name = (null)}
 3. 任务1---thread:{number = 4, name = (null)}---queue:
 4. 任务2---thread:{number = 4, name = (null)}---queue:
 5. 任务3---thread:{number = 4, name = (null)}---queue:
 */
- (void)asyncSerial2{
    //创建一个串行队列
    dispatch_queue_t queue = dispatch_queue_create("my queue2", DISPATCH_QUEUE_SERIAL);
    [NSThread detachNewThreadWithBlock:^{
        NSLog(@"---start---, thread: %@", [NSThread currentThread]);
        // 异步调用, 因此会为这个queue开启新线程, serial只会开启一个线程
        dispatch_async(queue, ^{
            NSLog(@"任务1---thread:%@---queue:%@", [NSThread currentThread], queue);
        });
        dispatch_async(queue, ^{
            NSLog(@"任务2---thread:%@---queue:%@", [NSThread currentThread], queue);
        });
        dispatch_async(queue, ^{
            NSLog(@"任务3---thread:%@---queue:%@", [NSThread currentThread], queue);
        });
        NSLog(@"---end---, thread: %@", [NSThread currentThread]);
    }];
}

结果解释:

  1. 先打印start, 然后打印end. async调用不会阻塞当前线程, 会跳过task, 继续向后执行.
  2. 使用async, 异步调用到 serial queue, 会创建一个线程.
  3. 使用串行队列, 队列中的 task 按照添加入的顺序, 顺序执行1,2,3.
iOS多线程 -- GCD相关学习笔记_第3张图片
异步执行 + 串行队列

3. 同步执行 + 并行队列

/*
 主线程 + 同步执行 + 并行队列
 1. ---start---, thread: {number = 1, name = main}
 2. 任务1---thread:{number = 1, name = main}---queue:
 3. 任务2---thread:{number = 1, name = main}---queue:
 4. 任务3---thread:{number = 1, name = main}---queue:
 5. ---end---, thread: {number = 1, name = main}
 */
- (void)syncConcurrent1{
    //创建一个并行队列
    dispatch_queue_t queue = dispatch_queue_create("my queue2.1", DISPATCH_QUEUE_CONCURRENT);
    NSLog(@"---start---, thread: %@", [NSThread currentThread]);
    dispatch_sync(queue, ^{
        NSLog(@"任务1---thread:%@---queue:%@", [NSThread currentThread], queue);
    });
    dispatch_sync(queue, ^{
        NSLog(@"任务2---thread:%@---queue:%@", [NSThread currentThread], queue);
    });
    dispatch_sync(queue, ^{
        NSLog(@"任务3---thread:%@---queue:%@", [NSThread currentThread], queue);
    });
    NSLog(@"---end---, thread: %@", [NSThread currentThread]);
}

/*
 子线程 + 同步执行 + 并行队列
 1. ---start---, thread:{number = 3, name = (null)}
 2. 任务1---thread:{number = 3, name = (null)}---queue:
 3. 任务2---thread:{number = 3, name = (null)}---queue:
 4. 任务3---thread:{number = 3, name = (null)}---queue:
 5. ---end---, thread: {number = 3, name = (null)}
 */
- (void)syncConcurrent2{
    //创建一个并行队列
    dispatch_queue_t queue = dispatch_queue_create("my queue2.2", DISPATCH_QUEUE_CONCURRENT);
    [NSThread detachNewThreadWithBlock:^{
        NSLog(@"---start---, thread:%@", [NSThread currentThread]);
        //使用同步函数封装三个任务
        dispatch_sync(queue, ^{
            NSLog(@"任务1---thread:%@---queue:%@", [NSThread currentThread], queue);
        });
        dispatch_sync(queue, ^{
            NSLog(@"任务2---thread:%@---queue:%@", [NSThread currentThread], queue);
        });
        dispatch_sync(queue, ^{
            NSLog(@"任务3---thread:%@---queue:%@", [NSThread currentThread], queue);
        });

        NSLog(@"---end---, thread: %@", [NSThread currentThread]);
    }];
}

结果解释:

  1. 同步执行(sync), 不会创建新线程, 会在调用dispatch_sync函数的当前线程运行task.
  2. 并行队列, 并发功能在同步执行中无效, 所有task按照添加顺序一个个顺序执行.
  3. 整个顺序是在当前线程执行: start -> task1 -> task2 -> task3 -> end

4. 同步执行 + 串行队列

/*
 主线程 + 同步执行 + 串行队列

 1. ---start---, thread:{number = 1, name = main}
 2. 任务1---thread:{number = 1, name = main}---queue:
 3. 任务2---thread:{number = 1, name = main}---queue:
 4. 任务3---thread:{number = 1, name = main}---queue:
 5. ---end---, thread: {number = 1, name = main}
 */
- (void)syncSerial1{
    //创建一个并行队列
    dispatch_queue_t queue = dispatch_queue_create("my queue3.1", DISPATCH_QUEUE_SERIAL);
    // 创建一个子线程, 然后去 同步执行+ 并行队列
    NSLog(@"---start---, thread:%@", [NSThread currentThread]);
    //使用同步函数封装三个任务
    dispatch_sync(queue, ^{
        NSLog(@"任务1---thread:%@---queue:%@", [NSThread currentThread], queue);
    });
    dispatch_sync(queue, ^{
        NSLog(@"任务2---thread:%@---queue:%@", [NSThread currentThread], queue);
    });
    dispatch_sync(queue, ^{
        NSLog(@"任务3---thread:%@---queue:%@", [NSThread currentThread], queue);
    });
    NSLog(@"---end---, thread: %@", [NSThread currentThread]);
}

/*
 子线程 + 同步执行 + 串行队列
 1. ---start---, thread:{number = 3, name = (null)}
 2. 任务1---thread:{number = 3, name = (null)}---queue:
 3. 任务2---thread:{number = 3, name = (null)}---queue:
 4. 任务3---thread:{number = 3, name = (null)}---queue:
 5. ---end---, thread: {number = 3, name = (null)}
 */
- (void)syncSerial2{
    //创建一个并行队列
    dispatch_queue_t queue = dispatch_queue_create("my queue3.1", DISPATCH_QUEUE_SERIAL);
    // 创建一个子线程, 然后去 同步执行+ 并行队列
    [NSThread detachNewThreadWithBlock:^{
        NSLog(@"---start---, thread:%@", [NSThread currentThread]);
        //使用同步函数封装三个任务
        dispatch_sync(queue, ^{
            NSLog(@"任务1---thread:%@---queue:%@", [NSThread currentThread], queue);
        });
        dispatch_sync(queue, ^{
            NSLog(@"任务2---thread:%@---queue:%@", [NSThread currentThread], queue);
        });
        dispatch_sync(queue, ^{
            NSLog(@"任务3---thread:%@---queue:%@", [NSThread currentThread], queue);
        });

        NSLog(@"---end---, thread: %@", [NSThread currentThread]);
    }];
}

结果解释:

  1. 同步执行(sync), 不会创建新线程, 会在调用dispatch_sync函数的当前线程(主线程或者子线程)运行task.
  2. 串行队列, 所有task按照添加顺序一个个顺序执行.
  3. 整个顺序是在当前线程执行: start -> task1 -> task2 -> task3 -> end

5. 同步执行 + mainQueue

/*
 主线程 + 同步执行 + mainQueue

 结果死锁
 */
- (void)syncInMainQueue1{
    //创建一个并行队列
    dispatch_queue_t queue = dispatch_get_main_queue();
    // 创建一个子线程, 然后去 同步执行+ 并行队列
    NSLog(@"---start---, thread:%@", [NSThread currentThread]);
    //使用同步函数封装三个任务
    dispatch_sync(queue, ^{
        NSLog(@"任务1---thread:%@---queue:%@", [NSThread currentThread], queue);
    });
    dispatch_sync(queue, ^{
        NSLog(@"任务2---thread:%@---queue:%@", [NSThread currentThread], queue);
    });
    dispatch_sync(queue, ^{
        NSLog(@"任务3---thread:%@---queue:%@", [NSThread currentThread], queue);
    });
    NSLog(@"---end---, thread: %@", [NSThread currentThread]);
}

/*
 子线程中 + 同步 + mainQueue

 1. ---start---, thread:{number = 3, name = (null)}
 2. 任务1---thread:{number = 1, name = main}---queue:
 3. 任务2---thread:{number = 1, name = main}---queue:
 4. 任务3---thread:{number = 1, name = main}---queue:
 5. ---end---, thread: {number = 3, name = (null)}
 */
- (void)syncInMainQueue2{
    //创建一个并行队列
    [NSThread detachNewThreadWithBlock:^{
        //创建一个并行队列
        dispatch_queue_t queue = dispatch_get_main_queue();
        // 创建一个子线程, 然后去 同步执行+ 并行队列
        NSLog(@"---start---, thread:%@", [NSThread currentThread]);
        //使用同步函数封装三个任务
        dispatch_sync(queue, ^{
            NSLog(@"任务1---thread:%@---queue:%@", [NSThread currentThread], queue);
        });
        dispatch_sync(queue, ^{
            NSLog(@"任务2---thread:%@---queue:%@", [NSThread currentThread], queue);
        });
        dispatch_sync(queue, ^{
            NSLog(@"任务3---thread:%@---queue:%@", [NSThread currentThread], queue);
        });
        NSLog(@"---end---, thread: %@", [NSThread currentThread]);
    }];
}
iOS多线程 -- GCD相关学习笔记_第4张图片
主线程 + 同步调用发生死锁
iOS多线程 -- GCD相关学习笔记_第5张图片
image

结果解释:

  1. 主线程中同步会等到block执行完成才返回,sync在dispatch_get_main_queue() 队列中, 它是串行队列,sync是后加入的,前一个是主线程,所以 sync 想执行 block 必须等待主线程执行完成,主线程等待 sync 返回,去执行后续内容。因此, sync 等待mainThread 执行完成, mianThread 等待sync 函数返回, 两者互相等待, 造成死锁.
  2. 在子线程中调用时, 由于当前线程是子线程, 因此sync只会阻塞子线程, 待task在mainThread中执行完成以后,执行后面的内容.

6. 同步异步的嵌套情况

/**
 同步嵌套 + 串行队列的死锁

 1. ---start---, thread:{number = 1, name = main}
 2. 任务1---thread:{number = 1, name = main}---queue:
 3. 死锁!!!!!!!!!!!!!!(不会执行 end)
 */
-(void)deadLockTestSerialQueue{
    dispatch_queue_t queue = dispatch_queue_create("queue", DISPATCH_QUEUE_SERIAL);
    NSLog(@"---start---, thread:%@", [NSThread currentThread]);
    //使用同步函数封装三个任务
    dispatch_sync(queue, ^{
        NSLog(@"任务1---thread:%@---queue:%@", [NSThread currentThread], queue);
        dispatch_sync(queue, ^{
            NSLog(@"任务2---thread:%@---queue:%@", [NSThread currentThread], queue);
        });
        NSLog(@"任务3---thread:%@---queue:%@", [NSThread currentThread], queue);
    });

    NSLog(@"---end---, thread: %@", [NSThread currentThread]);
}

结果解释:

  1. 因为串行队列中线程是有执行顺序的,需要task1的同步任务执行完毕,才会执行下面开启的task2同步任务。而task1同步任务还没执行完,要到下面的大括号才算执行完毕,而task2同步任务已经在抢占资源了,就会发生死锁。

简单理解, 串行队列中, 有两个同步任务嵌套, 会导致第二个同步线程运行死锁.

/**
同步嵌套 + 并行队列 --> 正常执行

 1. ---start---, thread:{number = 1, name = main}
 2. 任务1---thread:{number = 1, name = main}---queue:
 3. 任务2---thread:{number = 1, name = main}---queue:
 4. sleep 5s---thread:{number = 1, name = main}---queue:
 5. 任务3---thread:{number = 1, name = main}---queue:
 6. sleep 5s---thread:{number = 1, name = main}---queue:
 7. ---end---, thread: {number = 1, name = main}
 */
-(void)deadLockTestConcurrentQueue{
    //创建一个并行队列
    dispatch_queue_t queue = dispatch_queue_create("queue", DISPATCH_QUEUE_CONCURRENT);
    // 创建一个子线程, 然后去 同步执行+ 并行队列
    NSLog(@"---start---, thread:%@", [NSThread currentThread]);
    //使用同步函数封装三个任务
    dispatch_sync(queue, ^{
        NSLog(@"任务1---thread:%@---queue:%@", [NSThread currentThread], queue);
        dispatch_sync(queue, ^{
            NSLog(@"任务2---thread:%@---queue:%@", [NSThread currentThread], queue);
            [NSThread sleepForTimeInterval:5];
            NSLog(@"sleep 5s---thread:%@---queue:%@ ", [NSThread currentThread], queue);
        });
        NSLog(@"任务3---thread:%@---queue:%@", [NSThread currentThread], queue);
        [NSThread sleepForTimeInterval:5];
        NSLog(@"sleep 5s---thread:%@---queue:%@", [NSThread currentThread], queue);
    });
    NSLog(@"---end---, thread: %@", [NSThread currentThread]);
}

结果解释:

  1. 同步调用, 会在当前线程中运行(不会创建子线程), 并且等待当前dispatch_sync函数调用返回. 使用并行队列, task1/task3 和 task2 的执行没有顺序, 因此会立即执行task2, 然后睡眠5s, 然后打印task3...最后执行end.

同步嵌套, 如果使用并行队列, 会正常运行, 不会造成死锁.

/**
    异步调用嵌套同步调用 + 串行队列 == 死锁 (同 主线程, 用同步mainQueue)

 1. ---start---, thread:{number = 1, name = main}
 2. ---end---, thread: {number = 1, name = main}
 3. 任务1---thread:{number = 3, name = (null)}---queue:
 4. 死锁!!!!!!!!!!!!!!
 */
-(void)deadLockTestSerialQueue2{
    //创建一个并行队列
    dispatch_queue_t queue = dispatch_queue_create("queue", DISPATCH_QUEUE_SERIAL);
    // 创建一个子线程, 然后去 同步执行+ 并行队列
    NSLog(@"---start---, thread:%@", [NSThread currentThread]);
    //使用同步函数封装三个任务
    dispatch_async(queue, ^{
        NSLog(@"任务1---thread:%@---queue:%@", [NSThread currentThread], queue);
        dispatch_sync(queue, ^{
            NSLog(@"任务2---thread:%@---queue:%@", [NSThread currentThread], queue);
            [NSThread sleepForTimeInterval:5];
            NSLog(@"sleep 5s---thread:%@---queue:%@ ", [NSThread currentThread], queue);
        });
        NSLog(@"任务3---thread:%@---queue:%@", [NSThread currentThread], queue);
        [NSThread sleepForTimeInterval:5];
        NSLog(@"sleep 5s---thread:%@---queue:%@", [NSThread currentThread], queue);
    });
    NSLog(@"---end---, thread: %@", [NSThread currentThread]);
}

结果解释:

  1. 由于queue是串行队列, 在串行队列中同步串行队列, 死锁

特别注意队列的类型,串行队列同步调用 很容易造成死锁

GCD中Target Queue作用

前面我们了解到, 通过 dispatch_sync可以同步执行重要的代码. 本节学习用target queue可以帮助我们将多个queue之间的block执行保持串行执行.

1. 使用dispatch_set_target_queue更改Dispatch Queue的执行优先级

dispatch_queue_create函数生成的dispatch queue不管是serial dispatch Queue还是Concurrent Dispatch Queue, 执行的优先级都与默认优先级的Global Dispatch queue相同,如果需要变更生成的Dispatch Queue的执行优先级则需要使用dispatch_set_target_queue函数.

- (void)testTeagerQueue1 {
    dispatch_queue_t serialQueue = dispatch_queue_create("com.oukavip.www",NULL);
    dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND,0);
    
    dispatch_set_target_queue(serialQueue, globalQueue);
    // 第一个参数为要设置优先级的queue,第二个参数是参照物,既将第一个queue的优先级和第二个queue的优先级设置一样。
}

2.使用dispatch_set_target_queue修改用户队列的目标队列,使多个serial queue在目标queue上一次只有一个执行.

/**
 理论上, 多个 serialQueue queue1,queue2,queue3, dispatch_async到一个并行队列中, 应该是分别在三个thread中并行执行的.但是这里设置 target_queue以后, 三个线程都在number=3的同一个线程中执行.

 多个serial queue在多个线程并行执行  --->   多个serial queue在同一个线程中串行执行

 1. ---start---, thread:{number = 1, name = main}
 2. ---end---, thread:{number = 1, name = main}
 3. 任务1 in:---thread:{number = 3, name = (null)}---queue:
 4. 任务1 out:---thread:{number = 3, name = (null)}---queue:
 5. 任务2 in:---thread:{number = 3, name = (null)}---queue:
 6. 任务2 out:---thread:{number = 3, name = (null)}---queue:
 7. 任务3 in:---thread:{number = 3, name = (null)}---queue:
 8. 任务3 out:---thread:{number = 3, name = (null)}---queue:

  使用场景: 一般都是把一个任务放到一个串行的queue中,如果这个任务被拆分了,被放置到多个串行的queue中,但实际还是需要这个任务同步执行,那么就会有问题,因为多个串行queue之间是并行的。这时候dispatch_set_target_queue将起到作用。(实际上这种类型的需求使用NSOperation更好)

 */
-(void)testTargetQueue1 {

    // 目标队列是 串行队列
    dispatch_queue_t targetQueue = dispatch_queue_create("test.target.queue", DISPATCH_QUEUE_SERIAL);

    dispatch_queue_t queue1 = dispatch_queue_create("test.1", DISPATCH_QUEUE_SERIAL);
    dispatch_queue_t queue2 = dispatch_queue_create("test.2", DISPATCH_QUEUE_SERIAL);
    dispatch_queue_t queue3 = dispatch_queue_create("test.3", DISPATCH_QUEUE_SERIAL);

    dispatch_set_target_queue(queue1, targetQueue);
    dispatch_set_target_queue(queue2, targetQueue);
    dispatch_set_target_queue(queue3, targetQueue);

    NSLog(@"---start---, thread:%@", [NSThread currentThread]);
    dispatch_async(queue1, ^{
        NSLog(@"任务1 in:---thread:%@---queue:%@", [NSThread currentThread], queue1);
        [NSThread sleepForTimeInterval:3.f];
        NSLog(@"任务1 out:---thread:%@---queue:%@", [NSThread currentThread], queue1);
    });
    dispatch_async(queue2, ^{
        NSLog(@"任务2 in:---thread:%@---queue:%@", [NSThread currentThread], queue2);
        [NSThread sleepForTimeInterval:3.f];
        NSLog(@"任务2 out:---thread:%@---queue:%@", [NSThread currentThread], queue2);
    });
    dispatch_async(queue3, ^{
        NSLog(@"任务3 in:---thread:%@---queue:%@", [NSThread currentThread], queue3);
        [NSThread sleepForTimeInterval:3.f];
        NSLog(@"任务3 out:---thread:%@---queue:%@", [NSThread currentThread], queue3);
    });

    NSLog(@"---end---, thread:%@", [NSThread currentThread]);

}

/**


 1. ---start---, thread:{number = 1, name = main}
 2. ---end---, thread:{number = 1, name = main}
 3. 任务1 in:---thread:{number = 3, name = (null)}---queue:
 4. 任务1 in:---thread:{number = 3, name = (null)}---queue:
 5. 任务2 in:---thread:{number = 3, name = (null)}---queue:
 6. 任务2 in:---thread:{number = 3, name = (null)}---queue:
 7. 任务3 in:---thread:{number = 3, name = (null)}---queue:
 8. 任务3 in:---thread:{number = 3, name = (null)}---queue:

 queue1是并行队列, queue2是串行队列.在都设置 串行队列targetQueue作为目标队列以后. 所有的异步调用dispatch_async到queue1, queue2的block, 都好像dispatch_async到targetQueue中执行一样.
 */
- (void)testTargetQueue2 {
    // target-queue是 串行队列
    dispatch_queue_t targetQueue = dispatch_queue_create("targetQueue", DISPATCH_QUEUE_SERIAL);


    dispatch_queue_t queue1 = dispatch_queue_create("queue1", DISPATCH_QUEUE_CONCURRENT);//串行队列
    dispatch_queue_t queue2 = dispatch_queue_create("queue1", DISPATCH_QUEUE_SERIAL);//并发队列
    //设置参考, targetQueue是参考队列.
    dispatch_set_target_queue(queue1, targetQueue);
    dispatch_set_target_queue(queue2, targetQueue);
    NSLog(@"---start---, thread:%@", [NSThread currentThread]);
    // 虽然是异步调用但是是顺序执行.因为targetQueue是serial, 创建队列的层次体系
    dispatch_async(queue1, ^{
        NSLog(@"任务1 in:---thread:%@---queue:%@", [NSThread currentThread], queue1);
        [NSThread sleepForTimeInterval:2.f];
        NSLog(@"任务1 in:---thread:%@---queue:%@", [NSThread currentThread], queue1);
    });
    dispatch_async(queue1, ^{
        NSLog(@"任务2 in:---thread:%@---queue:%@", [NSThread currentThread], queue1);
        [NSThread sleepForTimeInterval:1.f];
        NSLog(@"任务2 in:---thread:%@---queue:%@", [NSThread currentThread], queue1);
    });
    dispatch_async(queue2, ^{
        NSLog(@"任务3 in:---thread:%@---queue:%@", [NSThread currentThread], queue2);
        [NSThread sleepForTimeInterval:3.f];
        NSLog(@"任务3 in:---thread:%@---queue:%@", [NSThread currentThread], queue2);
    });
    NSLog(@"---end---, thread:%@", [NSThread currentThread]);
}

目标队列都是使用的串行队列, 多个队列设置targetQueue为一个串行队列以后, dispatch_async到该队列的内容, 就好像是dispatch_async到targeQueue中执行一样.

GCD中的 dispatch group

dispatch group是GCD中专门用来监听多个异步任务(block)的.

dispatch group用来管理各种block, 在被关联到group的block完全执行以后, 通过相关api, 就能够通知当前group, 被添加的block执行完.

group中有两种方式通知当前添加到group的task任务执行完成:

  1. dispatch_group_wait, 阻塞当前线程, 等待所有任务完成或者等待超, 同步执行block.
  2. dispatch_group_notify, 会异步执行block, 不会阻塞当前线程.

group也有两种方式将block关联到group中:

  1. 使用dispatch_group_async/dispatch_group_sync
  2. 使用dispatch_group_enter/dispatch_group_leave

注意以下要点:

  • dispatch_group_async等价于dispatch_group_enter()dispatch_group_leave()的组合。
  • dispatch_group_enter() 必须运行在 dispatch_group_leave() 之前。
  • dispatch_group_enter()dispatch_group_leave() 需要成对出现的

下面是两种通知方式:

/**
 1 ---start---, thread:{number = 1, name = main}
 2 ---end  1---, thread:{number = 1, name = main}

 // 3.1, 3.2 两步是并行执行的
 3.1 任务2 in:---thread:{number = 3, name = (null)}---queue:
 3.2 任务1 in:---thread:{number = 4, name = (null)}---queue:

 // 4 在3.1, 3.2两步执行完成以后才执行,
 4 ---end  2---, thread:{number = 1, name = main}


 */
- (void)dispatchGroupWaitDemo1 {
    dispatch_queue_t queue = dispatch_queue_create("queue",DISPATCH_QUEUE_CONCURRENT);
    dispatch_group_t group = dispatch_group_create();
    // 在group中添加任务block
    NSLog(@"---start---, thread:%@", [NSThread currentThread]);
    dispatch_group_async(group, queue, ^{
        [NSThread sleepForTimeInterval:3.f];
        NSLog(@"任务1 ---thread:%@---queue:%@", [NSThread currentThread], queue);
    });
    dispatch_group_async(group, queue, ^{
        NSLog(@"任务2 ---thread:%@---queue:%@", [NSThread currentThread], queue);
    });

    NSLog(@"---end  1---, thread:%@", [NSThread currentThread]);
    // 使用阻塞等待group中的内容执行完成
    dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
    NSLog(@"---end  2---, thread:%@", [NSThread currentThread]);
}

/**
 1  ---start---, thread:{number = 1, name = main}
 2  ---end  1---, thread:{number = 1, name = main}
 // 3.1, 3.2是并行执行的
 3.1 任务2 ---thread:{number = 3, name = (null)}---queue:
 3.2 任务1 ---thread:{number = 4, name = (null)}---queue:

 // 异步回调notify
 4 ---end  2---, thread:{number = 1, name = main}

 */
- (void)dispatchGroupWaitDemo2 {
    dispatch_queue_t queue = dispatch_queue_create("queue",DISPATCH_QUEUE_CONCURRENT);
    dispatch_group_t group = dispatch_group_create();
    NSLog(@"---start---, thread:%@", [NSThread currentThread]);
    // 在group中添加任务block.
    dispatch_group_async(group, queue, ^{
        [NSThread sleepForTimeInterval:2.f];
        NSLog(@"任务1 ---thread:%@---queue:%@", [NSThread currentThread], queue);
    });
    dispatch_group_async(group, queue, ^{
        NSLog(@"任务2 ---thread:%@---queue:%@", [NSThread currentThread], queue);
    });
    dispatch_group_notify(group, dispatch_get_main_queue(), ^{
        NSLog(@"---end  2---, thread:%@", [NSThread currentThread]);
    });
    NSLog(@"---end  1---, thread:%@", [NSThread currentThread]);
}


/**
 同dispatchGroupWaitDemo2, 只关心加入到group的block, 并不关心queue

 1 ---start---, thread:{number = 1, name = main}
 2 ---end  1---, thread:{number = 1, name = main}
 3.1 任务1 ---thread:{number = 3, name = (null)}---queue:
 3.2 任务2 ---thread:{number = 4, name = (null)}---queue:
 4 ---end  2---, thread:{number = 1, name = main}

 */
- (void)dispatchGroupWaitDemo3 {
    dispatch_queue_t queue1 = dispatch_queue_create("queue1",DISPATCH_QUEUE_CONCURRENT);
    dispatch_queue_t queue2 = dispatch_queue_create("queue2",DISPATCH_QUEUE_CONCURRENT);

    dispatch_group_t group = dispatch_group_create();
    NSLog(@"---start---, thread:%@", [NSThread currentThread]);
    // 在group中添加任务block.
    dispatch_group_async(group, queue1, ^{
        [NSThread sleepForTimeInterval:2.f];
        NSLog(@"任务1 ---thread:%@---queue:%@", [NSThread currentThread], queue1);
    });
    dispatch_group_async(group, queue2, ^{
        [NSThread sleepForTimeInterval:5.f];
        NSLog(@"任务2 ---thread:%@---queue:%@", [NSThread currentThread], queue2);
    });

    dispatch_group_notify(group, dispatch_get_main_queue(), ^{
        NSLog(@"---end  2---, thread:%@", [NSThread currentThread]);
    });
    NSLog(@"---end  1---, thread:%@", [NSThread currentThread]);
}

以下是使用dispatch_group_enter/dispatch_group_leave将block关联group.

/**
 1. ---start---, thread:{number = 1, name = main}
 2. ---end  1---, thread:{number = 1, name = main}
 3.1 任务1 ---thread:{number = 3, name = (null)}---queue:
 3.2 任务2 ---thread:{number = 4, name = (null)}---queue:
 4. ---end  2---, thread:{number = 1, name = main}

 */
- (void)dispatchGroupWaitDemo4 {

    dispatch_queue_t queue1 = dispatch_queue_create("queue1",DISPATCH_QUEUE_CONCURRENT);
    dispatch_queue_t queue2 = dispatch_queue_create("queue2",DISPATCH_QUEUE_CONCURRENT);

    dispatch_group_t group = dispatch_group_create();
    NSLog(@"---start---, thread:%@", [NSThread currentThread]);

    dispatch_group_enter(group);
    dispatch_async(queue1, ^{
        [NSThread sleepForTimeInterval:2.f];
        NSLog(@"任务1 ---thread:%@---queue:%@", [NSThread currentThread], queue1);
        dispatch_group_leave(group);
    });
    dispatch_group_enter(group);
    dispatch_async(queue2, ^{
        [NSThread sleepForTimeInterval:5.f];
        NSLog(@"任务2 ---thread:%@---queue:%@", [NSThread currentThread], queue2);
        dispatch_group_leave(group);
    });

     // 两种通知 group中 block执行完成的方式, 阻塞与非阻塞
     // dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
    dispatch_group_notify(group, dispatch_get_main_queue(), ^{
        NSLog(@"---end  2---, thread:%@", [NSThread currentThread]);
    });
    NSLog(@"---end  1---, thread:%@", [NSThread currentThread]);
}

dispatch_group_enterdispatch_group_leave必须成对出现

dispatch_group_enterdispatch_group_leave成对出现包裹block, 实际作用与dispatch_group_async一样, 写法不一样.

/**
 1. ---start---, thread:{number = 1, name = main}
 2. ---end  1---, thread:{number = 1, name = main}
 3.1 任务3 ---thread:{number = 3, name = (null)}---queue:
 3.2 任务1 ---thread:{number = 4, name = (null)}---queue:
 3.3 任务2 ---thread:{number = 5, name = (null)}---queue:
 4. ---end  2---, thread:{number = 1, name = main}
 */
- (void)dispatchGroupWaitDemo5 {

    dispatch_queue_t queue1 = dispatch_queue_create("queue1",DISPATCH_QUEUE_CONCURRENT);
    dispatch_queue_t queue2 = dispatch_queue_create("queue2",DISPATCH_QUEUE_CONCURRENT);

    dispatch_group_t group = dispatch_group_create();
    NSLog(@"---start---, thread:%@", [NSThread currentThread]);

    dispatch_group_enter(group);
    dispatch_async(queue1, ^{
        [NSThread sleepForTimeInterval:2.f];
        NSLog(@"任务1 ---thread:%@---queue:%@", [NSThread currentThread], queue1);
        dispatch_group_leave(group);
    });
    dispatch_group_enter(group);

    dispatch_async(queue2, ^{
        [NSThread sleepForTimeInterval:5.f];
        NSLog(@"任务2 ---thread:%@---queue:%@", [NSThread currentThread], queue2);
        dispatch_group_leave(group);
    });

    dispatch_group_async(group, queue2, ^{
        [NSThread sleepForTimeInterval:1.f];
        NSLog(@"任务3 ---thread:%@---queue:%@", [NSThread currentThread], queue2);
    });

    dispatch_group_notify(group, dispatch_get_main_queue(), ^{
        NSLog(@"---end  2---, thread:%@", [NSThread currentThread]);
    });
    NSLog(@"---end  1---, thread:%@", [NSThread currentThread]);
}

GCD中的 dispatch barrier

有时候,我们有这种需求, 有两组操作, 第一组操作都是并发执行, 第二组操作也是互相并发执行, 但是第一组操作和第二组操作之间, 需要完成一个些其他的操作,这组操作不能和其他两组操作并发.

iOS多线程 -- GCD相关学习笔记_第6张图片
barrier

一般有人将 barrier方法成为阻挡块, 会将dispatch_barrier_async之前加入queue的block先执行完,然后执行 barrier block方法, 然后执行barrier之后的block.

在学习了下一节内容时候, 我们就会知道dispatch_barrier_async方法实际是给传递给该方法的block, 增加了一个DISPATCH_BLOCK_BARRIER属性标记, 我们称这个block为阻挡块. 当有阻挡块标志的block对象直接调用时,此标志无效。

/**
 2018-07-04 14:42:32.529311+0800 pthreadDemo[56921:5025231] ---start---, thread:{number = 1, name = main}
 2018-07-04 14:42:32.529587+0800 pthreadDemo[56921:5025231] ---end---, thread:{number = 1, name = main}
 2018-07-04 14:42:34.534769+0800 pthreadDemo[56921:5025445] 2---{number = 3, name = (null)}
 2018-07-04 14:42:34.534782+0800 pthreadDemo[56921:5025447] 1---{number = 4, name = (null)}
 2018-07-04 14:42:36.538536+0800 pthreadDemo[56921:5025445] 2---{number = 3, name = (null)}
 2018-07-04 14:42:36.538536+0800 pthreadDemo[56921:5025447] 1---{number = 4, name = (null)}
 2018-07-04 14:42:38.540485+0800 pthreadDemo[56921:5025447] barrier---{number = 4, name = (null)}
 2018-07-04 14:42:40.543862+0800 pthreadDemo[56921:5025447] barrier---{number = 4, name = (null)}
 2018-07-04 14:42:42.545779+0800 pthreadDemo[56921:5025445] 4---{number = 3, name = (null)}
 2018-07-04 14:42:42.545780+0800 pthreadDemo[56921:5025447] 3---{number = 4, name = (null)}
 2018-07-04 14:42:44.547830+0800 pthreadDemo[56921:5025447] 3---{number = 4, name = (null)}
 2018-07-04 14:42:44.547830+0800 pthreadDemo[56921:5025445] 4---{number = 3, name = (null)}

 */
- (void)dispatchBarrierAsyncDemo {
    dispatch_queue_t queue = dispatch_queue_create("queue", DISPATCH_QUEUE_CONCURRENT);
    NSLog(@"---start---, thread:%@", [NSThread currentThread]);
    dispatch_async(queue, ^{
        // 追加任务1
        for (int i = 0; i < 2; ++i) {
            [NSThread sleepForTimeInterval:2];              // 模拟耗时操作
            NSLog(@"1---%@",[NSThread currentThread]);      // 打印当前线程
        }
    });
    dispatch_async(queue, ^{
        // 追加任务2
        for (int i = 0; i < 2; ++i) {
            [NSThread sleepForTimeInterval:2];              // 模拟耗时操作
            NSLog(@"2---%@",[NSThread currentThread]);      // 打印当前线程
        }
    });
    dispatch_barrier_async(queue, ^{
        // 追加任务 barrier
        for (int i = 0; i < 2; ++i) {
            [NSThread sleepForTimeInterval:2];              // 模拟耗时操作
            NSLog(@"barrier---%@",[NSThread currentThread]);// 打印当前线程
        }
    });

    dispatch_async(queue, ^{
        // 追加任务3
        for (int i = 0; i < 2; ++i) {
            [NSThread sleepForTimeInterval:2];              // 模拟耗时操作
            NSLog(@"3---%@",[NSThread currentThread]);      // 打印当前线程
        }
    });
    dispatch_async(queue, ^{
        // 追加任务4
        for (int i = 0; i < 2; ++i) {
            [NSThread sleepForTimeInterval:2];              // 模拟耗时操作
            NSLog(@"4---%@",[NSThread currentThread]);      // 打印当前线程
        }
    });

    NSLog(@"---end---, thread:%@", [NSThread currentThread]);

}

dispatch block

在iOS 8. GCD又增加了很多关于 dispatch_block的属性,以及api:

  • 在创建block以后, 可以在它为被调用之前取消
  • 通过设置成barrier block(同 dispatch_barrier_async), 打到阻塞块效果
  • 通过dispatch_block_notifydispatch_block_wait监听某block执行完成.
dispatch_block_create()
dispatch_block_cancel()
dispatch_block_wait()
dispatch_block_notify()
dispatch_block_testCancel()

dispatch_block 有以下dispatch_block_flags_t:

DISPATCH_ENUM_AVAILABLE_STARTING(__MAC_10_10, __IPHONE_8_0)
DISPATCH_ENUM(dispatch_block_flags, unsigned long
    DISPATCH_BLOCK_BARRIER = 0x01,
    DISPATCH_BLOCK_DETACHED = 0x02,
    DISPATCH_BLOCK_ASSIGN_CURRENT = 0x04,
    DISPATCH_BLOCK_NO_QOS_CLASS = 0x08,
    DISPATCH_BLOCK_INHERIT_QOS_CLASS = 0x10,
    DISPATCH_BLOCK_ENFORCE_QOS_CLASS = 0x20,
);
  • DISPATCH_BLOCK_BARRIER, 指示block对象在提交给并发队列时, 应该充当阻挡块的标志。有关详细信息,请参阅dispatch_barrier_async()。当调度块对象直接调用时,此标志无效。
  • DISPATCH_BLOCK_DETACHED, 指示block对象应当与当前执行上下文属性(如QOS类,os_activity_t和当前IPC请求属性(如果有))脱离关联的标志。如果直接调用,块对象将在调用线程的持续时间内(在应用分配给块对象的属性之前,如果有的话)从调用线程中移除这些属性。如果提交给队列,将使用队列的属性(或专门分配给块对象的任何属性)执行块对象。
  • 其他属性参考官方文档...

同时,也可以指定dispatch_qos_class_t, 指定dispatch_block 的优先级.

相关demo如下, 可以取消, 可以同步/异步等待某block执行完, 可以设置barrier操作.

/**
 2018-07-04 16:20:47.179561+0800 pthreadDemo[64925:5298793] ---start---, thread:{number = 1, name = main}
 2018-07-04 16:20:49.184556+0800 pthreadDemo[64925:5299137] 1---{number = 4, name = (null)}
 2018-07-04 16:20:49.184557+0800 pthreadDemo[64925:5299136] 2---{number = 3, name = (null)}
 2018-07-04 16:20:51.187729+0800 pthreadDemo[64925:5299137] 1---{number = 4, name = (null)}
 2018-07-04 16:20:51.187769+0800 pthreadDemo[64925:5299136] 2---{number = 3, name = (null)}
 2018-07-04 16:20:51.188237+0800 pthreadDemo[64925:5298793] ---end---, thread:{number = 1, name = main}
 2018-07-04 16:20:53.193472+0800 pthreadDemo[64925:5299136] 4---{number = 3, name = (null)}
 2018-07-04 16:20:53.193472+0800 pthreadDemo[64925:5299137] 3---{number = 4, name = (null)}
 2018-07-04 16:20:55.199081+0800 pthreadDemo[64925:5299137] 3---{number = 4, name = (null)}
 2018-07-04 16:20:55.199085+0800 pthreadDemo[64925:5299136] 4---{number = 3, name = (null)}

 */
-(void)dispatchBlockDemo1{
    dispatch_queue_t queue = dispatch_queue_create("queue", DISPATCH_QUEUE_CONCURRENT);
    NSLog(@"---start---, thread:%@", [NSThread currentThread]);
    dispatch_async(queue, ^{
        // 追加任务1
        for (int i = 0; i < 2; ++i) {
            [NSThread sleepForTimeInterval:2];              // 模拟耗时操作
            NSLog(@"1---%@",[NSThread currentThread]);      // 打印当前线程
        }
    });
    dispatch_async(queue, ^{
        // 追加任务2
        for (int i = 0; i < 2; ++i) {
            [NSThread sleepForTimeInterval:2];              // 模拟耗时操作
            NSLog(@"2---%@",[NSThread currentThread]);      // 打印当前线程
        }
    });

    // 创建一个 barrier block
    dispatch_block_t block = dispatch_block_create(DISPATCH_BLOCK_BARRIER, ^{
        // 追加任务 barrier
        for (int i = 0; i < 2; ++i) {
            [NSThread sleepForTimeInterval:2];              // 模拟耗时操作
            NSLog(@"barrier---%@",[NSThread currentThread]);// 打印当前线程
        }
    });
    dispatch_async(queue,block);

    dispatch_async(queue, ^{
        // 追加任务3
        for (int i = 0; i < 2; ++i) {
            [NSThread sleepForTimeInterval:2];              // 模拟耗时操作
            NSLog(@"3---%@",[NSThread currentThread]);      // 打印当前线程
        }
    });
    dispatch_async(queue, ^{
        // 追加任务4
        for (int i = 0; i < 2; ++i) {
            [NSThread sleepForTimeInterval:2];              // 模拟耗时操作
            NSLog(@"4---%@",[NSThread currentThread]);      // 打印当前线程
        }
    });

    dispatch_block_cancel(block);
    dispatch_block_wait(block, DISPATCH_TIME_FOREVER);

    NSLog(@"---end---, thread:%@", [NSThread currentThread]);
}

一个下载图片中途取消的实例:

func downloadPhotosWithCompletion(completion: BatchPhotoDownloadingCompletionClosure?) {
     var storedError: NSError!
     let downloadGroup = dispatch_group_create()
     var addresses = [OverlyAttachedGirlfriendURLString,
          SuccessKidURLString,
          LotsOfFacesURLString]
     addresses += addresses + addresses // 扩展address数组,复制3份
     var blocks: [dispatch_block_t] = [] // 一个保存block的数组

     for i in 0 ..< addresses.count {
          dispatch_group_enter(downloadGroup)
          let block = dispatch_block_create(DISPATCH_BLOCK_INHERIT_QOS_CLASS) { // 创建一个block,block的标志是DISPATCH_BLOCK_INHERIT_QOS_CLASS
               let index = Int(i)
               let address = addresses[index]
               let url = NSURL(string: address)
               let photo = DownloadPhoto(url: url!) {
                    image, error in
                    if let error = error {
                         storedError = error
                    }
                    dispatch_group_leave(downloadGroup)
               }
               PhotoManager.sharedManager.addPhoto(photo)
          }
          blocks.append(block)
          dispatch_async(GlobalMainQueue, block) // 把这个block放到GlobalMainQueue上异步调用。因为全局队列是一个顺序队列所以方便取消对象block,同时可以保证下载任务在downloadPhotosWithCompletion返回后才开始执行。
     }

     for block in blocks[3 ..< blocks.count] {
          let cancel = arc4random_uniform(2) // 随机返回一个整数,会返回0或1
          if cancel == 1 {
               dispatch_block_cancel(block) // 如果是1就取消block,这个只能发生在block还在队列中并没有开始的情况下。因为把block已经放到了GlobalMainQueue中,所以这个地方会先执行,执行完了才会执行block。
               dispatch_group_leave(downloadGroup) // 因为已经dispatch_group_enter了,所以取消时也要将其都leave掉。
          }
     }

     dispatch_group_notify(downloadGroup, GlobalMainQueue) {
          if let completion = completion {
               completion(error: storedError)
          }
     }
}

其他GCD中重要的内容

快速迭代 dispatch_apply

dispatch_apply函数是dispatch_sync函数和dispatch group的关联API,该函数按指定的次数, 将指定的block追加到指定的dispatch queue中, 并等到全部的处理执行结束.

/**
 1. 0---{number = 1, name = main}
 2. 1---{number = 4, name = (null)}
 3. 2---{number = 3, name = (null)}
 4. 3---{number = 5, name = (null)}
 5. 4---{number = 4, name = (null)}
 6. The end
 */
- (void)dispatchApplyDemo {
    dispatch_queue_t concurrentQueue = dispatch_queue_create("concurrentQueue", DISPATCH_QUEUE_CONCURRENT);
    dispatch_apply(5, concurrentQueue, ^(size_t i) {
        NSLog(@"%zu---%@",i,[NSThread currentThread]);
    });
    NSLog(@"The end"); //这里有个需要注意的是,dispatch_apply这个是会阻塞主线程的。这个log打印会在dispatch_apply都结束后才开始执行
}

由于直接使用concurrent queue进行dispatch_async调用, 会造成线程爆炸, 如果使用dispatch_apply, 就不会.

dispatch_after

dispatch_after是来延迟执行的GCD方法,因为在主线程中我们不能用sleep来延迟方法的调用,所以用dispatch_after是最合适的.

dispatch_after能让我们添加进队列的任务延时执行,该函数并不是在指定时间后执行处理,而只是在指定时间追加处理到dispatch_queue.

上面是用gcd实现的延时,除了gcd之外,还可以通过NSObject的分类方法:

  1. [self performSelector:@selector(doSomething) withObject:self afterDelay:2];
  1. NSTimer的类方法:[NSTimer scheduledTimerWithTimeInterval:2 target:self selector:@selector(doSomething) userInfo:nil repeats:NO];

它们在主线程上的执行效果是一样的。在子线程上的话,使用NSObject的分类方法和NSTimer的类方法就得注意了!它们的实现是基于runloop的.

dispatch_semaphore 信号量

dispatch_semaphore是GCD提供的一种保证同步的方式.

使用dispatch_semaphore_signal使得信号量+1, 使用dispatch_semaphore_wait使得信号量-1, 为0时的等待, 保证不同线程同步的目的.

/**
 1 start--- 1 {number = 1, name = main}
 2 start--- 2 {number = 3, name = (null)}
 3 semaphore +1---{number = 3, name = (null)}
 4 continue---{number = 1, name = main}

 */
- (void)dispatchSemaphoreDemo {
    //创建semaphore
    dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
    NSLog(@"start--- 1 %@", [NSThread currentThread]);
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        NSLog(@"start--- 2 %@", [NSThread currentThread]);
        [NSThread sleepForTimeInterval:1.f];
        NSLog(@"semaphore +1---%@", [NSThread currentThread]);
        dispatch_semaphore_signal(semaphore); //+1 semaphore
    });
    dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
    NSLog(@"continue---%@", [NSThread currentThread]);
}

虽然这里有更好的线程同步的方式, 比如 dispatch_sync, dispatch_barrier_async等等, 这里用信号量的方式可以同步的更加细腻.

其他的同步方法: 各种锁

参考文件

  1. iOS开发 - 多线程实现方案之GCD篇
  2. iOS-图文表并茂,手把手教你GCD
  3. 细说GCD(Grand Central Dispatch)如何用
  4. iOS GCD/主队列/并行队列/全局队列/串行队列/同步任务/异步任务区别 含代码

参考demo

https://github.com/brownfeng/GCDDemo

你可能感兴趣的:(iOS多线程 -- GCD相关学习笔记)