OC-多线程GCD

参考:
GCD源码
深入理解 GCD
iOS多线程--彻底学会多线程之『GCD』
关于iOS多线程,我说,你听,没准你就懂了

任务执行方式(同步、异步)

一般理解

1、同步执行(dispatch_sync):只能在当前线程中执行任务,不具备开启新线程的能力。必须等到Block函数执行完毕后,dispatch函数才会返回。
2、异步执行(dispatch_async):可以在新的线程中执行任务,具备开启新线程的能力。dispatch函数会立即返回, 然后Block在后台异步执行。

高级理解

1、dispatch_sync 的实现略简单一些,它不涉及线程池(因此一般都在当前线程执行),而是利用与线程绑定的信号量来实现串行
2、dispatch_async 会把任务添加到队列的一个链表中,添加完后会唤醒队列,根据 vtable 中的函数指针,调用 wakeup 方法。在 wakeup 方法中,从线程池里取出工作线程(如果没有就新建),然后在工作线程中取出链表头部指向的 block 并执行。

任务管理方式(串行队列、并行队列)

1、串行队列:所有任务会在一条线程中执行(有可能是当前线程也有可能是新开辟的线程),并且一个任务执行完毕后,才开始执行下一个任务。(等待完成)
2、并行队列:可以开启多条线程并行执行任务(但不一定会开启新的线程),并且当一个任务放到指定线程开始执行时,下一个任务就可以开始执行了。(等待发生)
3、分发到不同队列时,代码进入的分支也不一样,比如 dispatch_async 到主队列的任务由 runloop 处理,而分发到其他队列的任务由线程池处理

    // 主队列--串行,所有放在主队列中的任务,都会放到主线程中执行
    dispatch_queue_t mainQueue = dispatch_get_main_queue();
    
    // 全局队列--并行,系统为我们创建好的一个并行队列,使用起来与我们自己创建的并行队列无本质差别
    dispatch_queue_t globalQueue = dispatch_get_global_queue(0, 0);
    
    // new串行队列
    dispatch_queue_t queue1 = dispatch_queue_create("test.queue", DISPATCH_QUEUE_SERIAL);
    
    // new并行队列
    dispatch_queue_t queue2 = dispatch_queue_create("test.queue", DISPATCH_QUEUE_CONCURRENT);

注意:避免使用 GCD Global队列创建Runloop常驻线程
全局队列的底层是一个线程池,向全局队列中提交的 block,都会被放到这个线程池中执行,如果线程池已满,后续再提交 block 就不会再重新创建线程。等待有空闲的线程在执行任务。
所以:
避免使用 GCD Global 队列创建 Runloop 常驻线程,如果n条线程都被霸占了,Global队列就费了。

dispatch_group (组)

dispatch_group 的本质就是一个 value 非常大的信号量,等待 group 完成实际上就是等待 value 恢复初始值。而 notify 的作用是将所有注册的回调组装成一个链表,在 dispatch_async 完成时判断 value 是不是恢复初始值,如果是则调用 dispatch_async 异步执行所有注册的回调。

dispatch_once (单次)

dispatch_once 通过一个静态变量来标记 block 是否已被执行,同时使用信号量确保只有一个线程能执行,执行完 block 后会唤醒其他所有等待的线程

dispatch_barrier_async (栅栏)

dispatch_barrier_async 改变了 block 的 vtable 标记位,当它将要被取出执行时,会等待前面的 block 都执行完,然后在下一次循环中被执行

任务+队列

串行队列 并行队列 主队列
同步(sync) 当前线程,串行执行 队列当前线程,串行执行 主新线程,串行执行(注意死锁)
异步(async) 开1条新线程,串行执行 开n条新线程,异步执行(n在iphone7上面最大是几十个) 主新线程,串行执行
判断当前队列
    static char *queueKey = "queueKey";
    dispatch_queue_t queue1 = dispatch_queue_create(queueKey, nil);
    dispatch_queue_set_specific(queue, queueKey, &queueKey, NULL); // 设置标识
    dispatch_sync(queue1, ^{
        
        if (dispatch_get_specific(queueKey)) {
            //说明当前的队列就是queue1
        }else{
            //说明当前的队列不是是queue1
        }
    });
容易误解的概念

1、主线程只会执行主队列的任务--(主线程也可以执行其他队列,比如sync执行其他队列)

dispatch_queue_t mainQueue = dispatch_get_main_queue();
dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);

dispatch_queue_set_specific(mainQueue, "key", "main", NULL);
dispatch_sync(globalQueue, ^{
    BOOL res1 = [NSThread isMainThread];
    BOOL res2 = dispatch_get_specific("key") != NULL;
    
    NSLog(@"is main thread: %zd --- is main queue: %zd", res1, res2);
});

根据正常逻辑的理解来说,这里的两个判断结果应该都是NO,但运行后,第一个判断为YES,后者为NO,输出说明了主线程此时执行了work queue的任务.

Dispatch Block

队列执行任务都是block的方式,

创建block
- (void)createDispatchBlock {
    // 一般的block
    dispatch_queue_t concurrentQueue = dispatch_queue_create("com.starming.gcddemo.concurrentqueue",DISPATCH_QUEUE_CONCURRENT);
    dispatch_block_t block = dispatch_block_create(0, ^{
        NSLog(@"run block");
    });
    dispatch_async(concurrentQueue, block);

    //QOS优先级的block
    dispatch_block_t qosBlock = dispatch_block_create_with_qos_class(0, QOS_CLASS_USER_INITIATED, -1, ^{
        NSLog(@"run qos block");
    });
    dispatch_async(concurrentQueue, qosBlock);
}

dispatch_block_wait:可以根据dispatch block来设置等待时间,参数DISPATCH_TIME_FOREVER会一直等待block结束

- (void)dispatchBlockWaitDemo {
    dispatch_queue_t serialQueue = dispatch_queue_create("com.starming.gcddemo.serialqueue", DISPATCH_QUEUE_SERIAL);
    dispatch_block_t block = dispatch_block_create(0, ^{
        NSLog(@"star");
        [NSThread sleepForTimeInterval:5.f];
        NSLog(@"end");
    });
    dispatch_async(serialQueue, block);
    //设置DISPATCH_TIME_FOREVER会一直等到前面任务都完成
    dispatch_block_wait(block, DISPATCH_TIME_FOREVER);
    NSLog(@"ok, now can go on");
}

dispatch_block_notify:可以监视指定dispatch block结束,然后再加入一个block到队列中。三个参数分别为,第一个是需要监视的block,第二个参数是需要提交执行的队列,第三个是待加入到队列中的block

- (void)dispatchBlockNotifyDemo {
    dispatch_queue_t serialQueue = dispatch_queue_create("com.starming.gcddemo.serialqueue", DISPATCH_QUEUE_SERIAL);
    dispatch_block_t firstBlock = dispatch_block_create(0, ^{
        NSLog(@"first block start");
        [NSThread sleepForTimeInterval:2.f];
        NSLog(@"first block end");
    });
    dispatch_async(serialQueue, firstBlock);
    dispatch_block_t secondBlock = dispatch_block_create(0, ^{
        NSLog(@"second block run");
    });
    //first block执行完才在serial queue中执行second block
    dispatch_block_notify(firstBlock, serialQueue, secondBlock);
}

dispatch_block_cancel:iOS8之后可以调用dispatch_block_cancel来取消(需要注意必须用dispatch_block_create创建dispatch_block_t)
需要注意的是,未执行的可以用此方法cancel掉,若已经执行则cancel不了
如果想中断(interrupt)线程,可以使用dispatch_block_testcancel方法

- (void)dispatchBlockCancelDemo {
    dispatch_queue_t serialQueue = dispatch_queue_create("com.starming.gcddemo.serialqueue", DISPATCH_QUEUE_SERIAL);
    dispatch_block_t firstBlock = dispatch_block_create(0, ^{
        NSLog(@"first block start");
        [NSThread sleepForTimeInterval:2.f];
        NSLog(@"first block end");
    });
    dispatch_block_t secondBlock = dispatch_block_create(0, ^{
        NSLog(@"second block run");
    });
    dispatch_async(serialQueue, firstBlock);
    dispatch_async(serialQueue, secondBlock);
    //取消secondBlock
    dispatch_block_cancel(secondBlock);
}

1. 串行队列 + 同步执行

不会开启新线程,在当前线程执行任务。任务是串行的,执行完一个任务,再执行下一个任务

- (void)serialQueueSync{
    
    NSLog(@"0========%@",[NSThread currentThread]);
    dispatch_queue_t serialQueue = dispatch_queue_create("", DISPATCH_QUEUE_SERIAL);
    dispatch_sync(serialQueue, ^{
        NSLog(@"1========%@",[NSThread currentThread]);
    });
    dispatch_sync(serialQueue, ^{
        NSLog(@"2========%@",[NSThread currentThread]);
    });
    dispatch_sync(serialQueue, ^{
        NSLog(@"3========%@",[NSThread currentThread]);
    });
    NSLog(@"4========%@",[NSThread currentThread]);
    
    /*
     NSLog输出
     0========{number = 3, name = (null)}
     1========{number = 3, name = (null)}
     2========{number = 3, name = (null)}
     3========{number = 3, name = (null)}
     4========{number = 3, name = (null)}
     */
     
}
2. 串行队列 + 异步执行

开一个新线程,一个一个执行任务

    NSLog(@"0========%@",[NSThread currentThread]);
    dispatch_queue_t queue = dispatch_queue_create("", DISPATCH_QUEUE_SERIAL);
    dispatch_async(queue, ^{
        NSLog(@"1========%@",[NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"2========%@",[NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"3========%@",[NSThread currentThread]);
    });
    NSLog(@"4========%@",[NSThread currentThread]);
    
    /*
     NSLog输出
     0========{number = 1, name = main}
     4========{number = 1, name = main}
     1========{number = 3, name = (null)}
     2========{number = 3, name = (null)}
     3========{number = 3, name = (null)}
     */
3. 并行队列 + 同步执行

当前线程,一个一个执行任务

    NSLog(@"0========%@",[NSThread currentThread]);
    dispatch_queue_t queue = dispatch_queue_create("", DISPATCH_QUEUE_CONCURRENT);
    dispatch_sync(queue, ^{
        NSLog(@"1========%@",[NSThread currentThread]);
    });
    dispatch_sync(queue, ^{
        NSLog(@"2========%@",[NSThread currentThread]);
    });
    dispatch_sync(queue, ^{
        NSLog(@"3========%@",[NSThread currentThread]);
    });
    NSLog(@"4========%@",[NSThread currentThread]);
    
    /*
     NSLog输出
     0========{number = 3, name = (null)}
     1========{number = 3, name = (null)}
     2========{number = 3, name = (null)}
     3========{number = 3, name = (null)}
     4========{number = 3, name = (null)}
     */
4. 并行队列 + 异步执行

开多个线程,异步执行

    NSLog(@"0========%@",[NSThread currentThread]);
    dispatch_queue_t queue = dispatch_queue_create("", DISPATCH_QUEUE_CONCURRENT);
    dispatch_async(queue, ^{
        NSLog(@"1========%@",[NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"2========%@",[NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"3========%@",[NSThread currentThread]);
    });
    NSLog(@"4========%@",[NSThread currentThread]);
    
    /*
     NSLog输出
     0========{number = 1, name = main}
     4========{number = 1, name = main}
     2========{number = 4, name = (null)}
     1========{number = 3, name = (null)}
     3========{number = 5, name = (null)}
     */
5. 主队列 + 异步执行

主线程,同步执行

    NSLog(@"0========%@",[NSThread currentThread]);
    dispatch_queue_t queue = dispatch_get_main_queue();
    dispatch_async(queue, ^{
        NSLog(@"1========%@",[NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"2========%@",[NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"3========%@",[NSThread currentThread]);
    });
    NSLog(@"4========%@",[NSThread currentThread]);
    
    /*
     NSLog输出
     0========{number = 3, name = (null)}
     4========{number = 3, name = (null)}
     1========{number = 1, name = main}
     2========{number = 1, name = main}
     3========{number = 1, name = main}
     */
6. 主队列 + 同步执行 (不能在主队列这么用,死锁)

主线程,同步执行

    NSLog(@"0========%@",[NSThread currentThread]);
    dispatch_queue_t queue = dispatch_get_main_queue();
    dispatch_sync(queue, ^{
        NSLog(@"1========%@",[NSThread currentThread]);
    });
    dispatch_sync(queue, ^{
        NSLog(@"2========%@",[NSThread currentThread]);
    });
    dispatch_sync(queue, ^{
        NSLog(@"3========%@",[NSThread currentThread]);
    });
    NSLog(@"4========%@",[NSThread currentThread]);
    
    /*
     NSLog输出
     0========{number = 3, name = (null)}
     1========{number = 1, name = main}
     2========{number = 1, name = main}
     3========{number = 1, name = main}
     4========{number = 3, name = (null)}
     */

GCD其他用法

dispatch_after延时

1、time = 0,是直接调用异步dispatch_async
2、time > 0, 只是延时提交block,不是延时立刻执行。

    //2秒延时、在主队列
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 2.0 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
        
    });
dispatch_once与dispatch_once_t
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        //
    });

1、dispatch_once并不是简单的只执行一次那么简单
2、dispatch_once本质上可以接受多次请求,会对此维护一个请求链表
3、如果在block执行期间,多次进入调用同类的dispatch_once函数(即单例函数),会导致整体链表无限增长,造成永久性死锁。

递归互相嵌套,如下:

- (void)viewDidLoad {
    [super viewDidLoad];
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{ // 链表无限增长
        [self viewDidLoad];
    });
}

dispatch_once源码

static void
dispatch_once_f_slow(dispatch_once_t *val, void *ctxt, dispatch_function_t func)
{
#if DISPATCH_GATE_USE_FOR_DISPATCH_ONCE
    dispatch_once_gate_t l = (dispatch_once_gate_t)val;

    if (_dispatch_once_gate_tryenter(l)) {
        _dispatch_client_callout(ctxt, func);
        _dispatch_once_gate_broadcast(l);
    } else {
        _dispatch_once_gate_wait(l);
    }
#else
    _dispatch_once_waiter_t volatile *vval = (_dispatch_once_waiter_t*)val;
    struct _dispatch_once_waiter_s dow = { };
    _dispatch_once_waiter_t tail = &dow, next, tmp;
    dispatch_thread_event_t event;


    if (os_atomic_cmpxchg(vval, NULL, tail, acquire)) {
        
        // 第一次dispatch_once,原子性操作
        
        // 当前线程
        dow.dow_thread = _dispatch_tid_self();
        // 执行block
        _dispatch_client_callout(ctxt, func);

        // 第一次执行完了,设置token = DISPATCH_ONCE_DONE
        next = (_dispatch_once_waiter_t)_dispatch_once_xchg_done(val);
        while (next != tail) {
            
            // 继续去下一个
            tmp = (_dispatch_once_waiter_t)_dispatch_wait_until(next->dow_next);
            event = &next->dow_event;
            next = tmp;
            
            // 信号量++
            _dispatch_thread_event_signal(event);
        }
    } else {
        
        // 第二次dispatch_once进来
        _dispatch_thread_event_init(&dow.dow_event);
        next = *vval;
        for (;;) {
            if (next == DISPATCH_ONCE_DONE) { // token是否等于DISPATCH_ONCE_DONE
                // 第一次执行完之后,都是走这里
                break;
            }
            // 如果是嵌套使用,第一次没有完成,又要执行一次
            if (os_atomic_cmpxchgv(vval, next, tail, &next, release)) {
                // 原子性
                dow.dow_thread = next->dow_thread;
                dow.dow_next = next;
                if (dow.dow_thread) {
                    pthread_priority_t pp = _dispatch_get_priority();
                    _dispatch_thread_override_start(dow.dow_thread, pp, val);
                }
                // 等待信号量
                _dispatch_thread_event_wait(&dow.dow_event);
                if (dow.dow_thread) {
                    _dispatch_thread_override_end(dow.dow_thread, val);
                }
                break;
            }
        }
        _dispatch_thread_event_destroy(&dow.dow_event);
    }
#endif
}
dispatch_apply(count,queue,block(index))迭代方法

该函数按指定的次数将指定的block追加到指定的队列;使用的地方,阻塞当前线程

    NSLog(@"CurrentThread------%@", [NSThread currentThread]);
    //dispatch_queue_t queue = dispatch_get_global_queue(0,0);
    dispatch_queue_t queue = dispatch_queue_create("test.queue", DISPATCH_QUEUE_SERIAL);
    // 6是次数
    dispatch_apply(6, queue, ^(size_t index) {
        NSLog(@"%zd------%@",index, [NSThread currentThread]);
    });
    /*
     并发队列:开多线程异步执行
     NSLogx信息
     CurrentThread------{number = 3, name = (null)}
     0------{number = 3, name = (null)}
     1------{number = 4, name = (null)}
     2------{number = 5, name = (null)}
     3------{number = 6, name = (null)}
     4------{number = 3, name = (null)}
     5------{number = 4, name = (null)}
     */
    
    
    /*
     同步队列:当前线程同步执行
     NSLogx信息
     CurrentThread------{number = 3, name = (null)}
     0------{number = 1, name = main}
     1------{number = 1, name = main}
     2------{number = 1, name = main}
     3------{number = 1, name = main}
     4------{number = 1, name = main}
     5------{number = 1, name = main}
     */

dispatch_apply能避免线程爆炸,因为GCD会管理并发

dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
for (int i = 0; i < 999; i++){
      dispatch_async(queue, ^{
         NSLog(@"%d,%@",i,[NSThread currentThread]);// 能开多大线程就开多大线程(几十个)
      });
}
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_apply(999, queue, ^(size_t i){
     NSLog(@"%d,%@",i,[NSThread currentThread]); // 只开一定数量的线程(几个)
});
dispatch_suspend、dispatch_resume (用在dispatch_get_global_queue主队列无效)

dispatch_suspend,dispatch_resume提供了“挂起、恢复”队列的功能,简单来说,就是可以暂停、恢复队列上的任务。但是这里的“挂起”,并不能保证可以立即停止队列上正在运行的block

注意点:

1、如果队列没有使用dispatch_suspend,使用dispatch_resume会crash
- (void)viewDidLoad {
    [super viewDidLoad];
    
    dispatch_queue_t queue = dispatch_queue_create("", DISPATCH_QUEUE_CONCURRENT);
    dispatch_resume(queue); // crash
}
2、如果queue被挂起,queue销毁时候没有被唤醒,会crash
        dispatch_queue_t queue = dispatch_queue_create("", DISPATCH_QUEUE_CONCURRENT);
        dispatch_suspend(queue);// 如果queue被挂起,queue销毁时候没有被唤醒,会crash
3、dispatch_suspend后面执行dispatch_sync,阻塞当前线程,需要其他线程恢复队列
        queue = dispatch_queue_create("", DISPATCH_QUEUE_CONCURRENT);
        dispatch_suspend(queue);
        // 后面执行dispatch_sync,阻塞当前线程,需要其他线程恢复队列
        dispatch_sync(queue, ^{
            NSLog(@"=====%@",@"111111");
        });
        NSLog(@"=====%@",@"22222");

GCD的队列组dispatch_group_t,其实就是封装了一个无限大的信号量,

注意事项
1、dispatch_group_async(只有async,无sync)等价于{dispatch_group_enter() + async}, async调用完了会执行dispatch_group_leave()。
2、dispatch_group_enter()就是信号量--;
3、dispatch_group_leave()就是信号量++
4、dispatch_group_enter() 必须运行在 dispatch_group_leave() 之前。
5、dispatch_group_enter() 和 dispatch_group_leave() 需要成对出现的

    //1.创建队列组
    dispatch_group_t group = dispatch_group_create();

    //2.1.全局队列
    dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        for (NSInteger i = 0; i < 3; i++) {
            NSLog(@"group-01 - %@", [NSThread currentThread]);
        }
    });
    
    //2.2.主队列
    dispatch_group_async(group, dispatch_get_main_queue(), ^{
        for (NSInteger i = 0; i < 3; i++) {
            NSLog(@"group-02 - %@", [NSThread currentThread]);
        }
    });
   
    //2.3.自建串行队列
    dispatch_group_async(group, dispatch_queue_create("test.queue", DISPATCH_QUEUE_SERIAL), ^{
        for (NSInteger i = 0; i < 3; i++) {
            NSLog(@"group-03 - %@", [NSThread currentThread]);
        }
    });
    
    //3.都完成后会自动通知,不阻塞当前线程
    dispatch_group_notify(group, dispatch_get_main_queue(), ^{
        NSLog(@"完成 - %@", [NSThread currentThread]);
    });
    NSLog(@"当前线程=====%@",[NSThread currentThread]);
    
     /*
      并行队列、自建串行队列的任务多线程异步执行
      主队列的任务主线程同步执行,且排在全部任务的最后
      
      NSLog信息
      当前线程====={number = 1, name = main}
      group-01 - {number = 3, name = (null)}
      group-01 - {number = 3, name = (null)}
      group-03 - {number = 4, name = (null)}
      group-01 - {number = 3, name = (null)}
      group-03 - {number = 4, name = (null)}
      group-03 - {number = 4, name = (null)}
      group-02 - {number = 1, name = main}
      group-02 - {number = 1, name = main}
      group-02 - {number = 1, name = main}
      完成 - {number = 1, name = main}
      */
手动标记group完成
  • dispatch_group_enter(group)
  • dispatch_group_leave(group);
dispatch_group_notify(不阻塞)相当于把block任务加在最后
NSLog(@"start");
    //1.创建队列组
    dispatch_group_t group = dispatch_group_create();
    for (int i=0; i< 5; i++) {
        dispatch_group_enter(group); // enter
        dispatch_async(dispatch_get_global_queue(0, 0), ^{
            // something
            NSLog(@"something===%zd",i);
            dispatch_group_leave(group); // eave
        });
    }
    // 都完成后会自动通知,不阻塞当前线程
    dispatch_group_notify(group, dispatch_get_main_queue(), ^{
        NSLog(@"完成 - %@", [NSThread currentThread]);
    });
    NSLog(@"end");

     /*
      NSLog信息:
      start
      end
      something===1
      something===0
      something===2
      something===3
      something===4
      完成 - {number = 1, name = main}
      */
    

dispatch_group_wait就是等待group的信号量回到初始值(阻塞当前线程)
    NSLog(@"start");
    //1.创建队列组
    dispatch_group_t group = dispatch_group_create();
    
    for (int i=0; i< 5; i++) {
        
        dispatch_group_enter(group); // enter
        
        dispatch_async(dispatch_get_global_queue(0, 0), ^{
            
            // something
            
            [NSThread sleepForTimeInterval:2];
            NSLog(@"something===%zd",i);
            dispatch_group_leave(group); // eave
        });
        
    }
    // 阻塞当前线程的、等待5秒
    dispatch_time_t waitTime = dispatch_time(DISPATCH_TIME_NOW, 5 * NSEC_PER_SEC);
    dispatch_group_wait(group, waitTime);//特殊值有:DISPATCH_TIME_FOREVER是无限等,DISPATCH_TIME_NOW是不等
    NSLog(@"end");
    
    
     /*
      等待时间 < 执行需要时间
      NSLog信息:
      start
      end
      something===0
      something===1
      something===3
      something===2
      something===4
      */
    
    /*
     等待时间 > 执行需要时间
     NSLog信息:
     start
     something===1
     something===0
     something===4
     something===2
     something===3
     end
     */
dispatch_semaphore_create & dispatch_semaphore_signal & dispatch_semaphore_wait

信号量是控制任务执行的重要条件,当信号量为0时,所有任务等待,信号量越大,允许可并行执行的任务数量越多。

  • dispatch_semaphore_create(long value);创建信号量,初始值不能小于0;value信号数值
  • dispatch_semaphore_wait(semaphore, timeout);等待降低信号量,也就是信号量-1;timeout不是调用dispatch_semaphore_wait后等待的时间,而是信号量创建后的时间
  • dispatch_semaphore_signal(semaphore);提高信号量,也就是信号量+1;
  • dispatch_semaphore_wait和dispatch_semaphore_signal通常配对使用。
    // 相当于控制新建的线程数
    dispatch_semaphore_t semaphore = dispatch_semaphore_create(10);
    for (int i=0; i< 10; i++) {
        
        dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
        dispatch_async(dispatch_get_global_queue(0, 0), ^{
            
            [NSThread sleepForTimeInterval:1];
            NSLog(@"第%@次_%@",@(i),[NSThread currentThread]);
            dispatch_semaphore_signal(semaphore);
        });
        
    }
    
    
     /*
      dispatch_semaphore_t semaphore = dispatch_semaphore_create(5);
      只有5条线程
      NSLog信息:
      第0次_{number = 4, name = (null)}
      第1次_{number = 6, name = (null)}
      第3次_{number = 5, name = (null)}
      第2次_{number = 3, name = (null)}
      第4次_{number = 7, name = (null)}
      第8次_{number = 3, name = (null)}
      第7次_{number = 5, name = (null)}
      第6次_{number = 4, name = (null)}
      第5次_{number = 6, name = (null)}
      第9次_{number = 7, name = (null)}
      */
    
    /*
     dispatch_semaphore_t semaphore = dispatch_semaphore_create(10);
     10条线程
     NSLog信息:
     第2次_{number = 3, name = (null)}
     第4次_{number = 7, name = (null)}
     第1次_{number = 4, name = (null)}
     第5次_{number = 8, name = (null)}
     第3次_{number = 6, name = (null)}
     第0次_{number = 5, name = (null)}
     第6次_{number = 9, name = (null)}
     第9次_{number = 10, name = (null)}
     第7次_{number = 11, name = (null)}
     第8次_{number = 12, name = (null)}
     */
dispatch_barrier_async、dispatch_barrier_sync (承上启下--用于自建的并行队列)

保证此前的任务都先于自己执行,此后的任务也迟于自己执行。
dispatch_barrier_async 不阻塞当前线程;
dispatch_barrier_sync 阻塞当前线程;

注意:dispatch_barrier_(a)sync只在自己创建的并发队列上有效,在全局(Global)并发队列、串行队列上,效果跟dispatch_(a)sync效果一样。

- (void)test{

    dispatch_queue_t globalQueue = dispatch_get_global_queue(0, 0);
    dispatch_async(globalQueue, ^{
        NSLog(@"任务1");
    });
    dispatch_async(globalQueue, ^{
        NSLog(@"任务2");
    });
    dispatch_barrier_async(globalQueue, ^{
        NSLog(@"任务barrier");
    });
    dispatch_async(globalQueue, ^{
        NSLog(@"任务3");
    });
    dispatch_async(globalQueue, ^{
        NSLog(@"任务4");
    });
    /*
     2017-09-02 21:03:40.255 NSThreadTest[28856:21431532] 任务2
     2017-09-02 21:03:40.255 NSThreadTest[28856:21431535] 任务1
     2017-09-02 21:03:40.255 NSThreadTest[28856:21431533] 任务barrier
     2017-09-02 21:03:40.255 NSThreadTest[28856:21431551] 任务3
     2017-09-02 21:03:40.256 NSThreadTest[28856:21431550] 任务4
     */
}

GCD创建Timer

-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
    //创建队列
    dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
    //1.创建一个GCD定时器
    /*
     第一个参数:表明创建的是一个定时器
     第四个参数:队列
     */
    dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
    // 需要对timer进行强引用,保证其不会被释放掉,才会按时调用block块
    // 局部变量,让指针强引用
    self.timer = timer;
    //2.设置定时器的开始时间,间隔时间,精准度
    /*
     第1个参数:要给哪个定时器设置
     第2个参数:开始时间
     第3个参数:间隔时间
     第4个参数:精准度 一般为0 在允许范围内增加误差可提高程序的性能
     GCD的单位是纳秒 所以要*NSEC_PER_SEC
     */
    dispatch_source_set_timer(timer, DISPATCH_TIME_NOW, 2.0 * NSEC_PER_SEC, 0 * NSEC_PER_SEC);

    //3.设置定时器要执行的事情
    dispatch_source_set_event_handler(timer, ^{
        NSLog(@"---%@--",[NSThread currentThread]);
    });
    // 启动
    dispatch_resume(timer);
}

GCD各种死锁的情况
1、最常见的
- (void)viewDidLoad {
    [super viewDidLoad];
    
    NSLog(@"0========%@",[NSThread currentThread]);// 当前是主线程、主队列
    dispatch_sync(dispatch_get_main_queue(), ^{
        NSLog(@"1========%@",[NSThread currentThread]);
    });
    NSLog(@"2========%@",[NSThread currentThread]);
    
    /*
     NSLog信息
     0========{number = 1, name = main}
     
     解析
     1.viewDidLoad是主队列,dispatch_sync也属于主队列,
     2.dispatch_sync是viewDidLoad里面的代码,viewDidLoad需要等待dispatch_sync执行完,dispatch_sync需要等待viewDidLoad执行完,这就死锁了
     */
    
}
- (void)viewDidLoad {
    [super viewDidLoad];
    
    NSLog(@"0========%@",[NSThread currentThread]);// 当前是主线程、主队列
    // 改成dispatch_get_global_queue或者new出来的队列
    dispatch_sync(dispatch_get_global_queue(0,0), ^{
        NSLog(@"1========%@",[NSThread currentThread]);
    });
    NSLog(@"2========%@",[NSThread currentThread]);
    
    /*
     NSLog信息
     0========{number = 1, name = main}
     1========{number = 1, name = main}
     2========{number = 1, name = main}
     
     解析
     1.viewDidLoad是主队列,dispatch_sync是global_queue,不在同一队列
     2.dispatch_sync是viewDidLoad里面的代码,viewDidLoad需要等待dispatch_sync执行完返回,但是dispatch_sync不需要等待viewDidLoad执行完,立即执行完返回
     */
    
}
2、串行队列,各种嵌套异步情况

死锁的原因:是同一个串行队列任务内部代码继续嵌套同步sync的任务

        // 串行队列
    dispatch_queue_t queue = dispatch_queue_create("", DISPATCH_QUEUE_SERIAL);
    
    // 同步嵌异步----执行OK
    dispatch_sync(queue, ^{
        dispatch_async(queue, ^{
        });
    });
    
    // 异步嵌异步----执行OK
    dispatch_async(queue, ^{
        dispatch_async(queue, ^{
        });
    });
    
    // 异步嵌同步----死锁
    dispatch_async(queue, ^{
        dispatch_sync(queue, ^{
        });
    });
    
    // 同步嵌同步----死锁
    dispatch_sync(queue, ^{
        dispatch_sync(queue, ^{
        });
    });

3、并行队列,各种嵌套异步情况

并行队列队列各种嵌套都不会死锁

    // 并行队列
    dispatch_queue_t queue = dispatch_queue_create("", DISPATCH_QUEUE_CONCURRENT);
    
    // 同步嵌异步----执行OK
    dispatch_sync(queue, ^{
        dispatch_async(queue, ^{
        });
    });
    
    // 异步嵌异步----执行OK
    dispatch_async(queue, ^{
        dispatch_async(queue, ^{
        });
    });
    
    // 异步嵌同步----执行OK
    dispatch_async(queue, ^{
        dispatch_sync(queue, ^{
        });
    });
    
    // 同步嵌同步----执行OK
    dispatch_sync(queue, ^{
        dispatch_sync(queue, ^{
        });
    });
4、dispatch_apply阻塞当前线程
// 主队列使用,死锁
dispatch_apply(5, dispatch_get_main_queue(), ^(size_t i) {
        NSLog(@"第%@次_%@",@(i),[NSThread currentThread]);
    });
// 嵌套使用,死锁
dispatch_queue_t queue = dispatch_queue_create("queue", DISPATCH_QUEUE_CONCURRENT);
dispatch_apply(10, queue, ^(size_t) {
    // 任务
    ...
    dispatch_apply(10, queue, ^(size_t) {
        // 任务
        ...
    });
});

dispatch_barrier
dispatch_barrier_sync在串行队列和全局并行队列里面和dispatch_sync同样的效果,所以需考虑同dispatch_sync一样的死锁问题。

5、 信号量阻塞主线程
- (void)viewDidLoad {
    [super viewDidLoad];

    dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
    NSLog(@"semaphore create!");
    dispatch_async(dispatch_get_main_queue(), ^{
        dispatch_semaphore_signal(semaphore);
        NSLog(@"semaphore plus 1");
    });
    dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
    NSLog(@"semaphore minus 1");
}

原因:
如果当前执行的线程是主线程,以上代码就会出现死锁。
因为dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER)阻塞了当前线程,而且等待时间是DISPATCH_TIME_FOREVER——永远等待,这样它就永远的阻塞了当前线程——主线程。导致主线中的dispatch_semaphore_signal(semaphore)没有执行,
而dispatch_semaphore_wait一直在等待dispatch_semaphore_signal改变信号量,这样就形成了死锁。

解决方法:
应该将信号量移到并行队列中,如全局调度队列。以下场景,移到串行队列也是可以的。但是串行队列还是有可能死锁的(如果执行dispatch_semaphore_signal方法还是在对应串行队列中的话,即之前提到的串行队列嵌套串行队列的场景)。

- (void)viewDidLoad {
    [super viewDidLoad];
    
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
        NSLog(@"semaphore create!");
        dispatch_async(dispatch_get_main_queue(), ^{
            dispatch_semaphore_signal(semaphore); // +1
            NSLog(@"semaphore plus 1");
        });
        dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
        NSLog(@"semaphore minus 1");
    });
}
一些嵌套使用问题
    NSLog(@"1");
    dispatch_sync(dispatch_get_global_queue(0, 0), ^{
        NSLog(@"2");
        dispatch_async(dispatch_get_main_queue(), ^{
           sleep(1);
            NSLog(@"3");
        });
    });
    dispatch_async(dispatch_get_main_queue(), ^{
        NSLog(@"4");
    });
    NSLog(@"5");

    //结果是:12534
    //解析:“2”并发队列同步任务,所以125;“3”、“4”是两个主队列异步,串行执行任务34;最终就是12534
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    dispatch_sync(queue, ^{
        
        dispatch_sync(queue, ^{
            NSLog(@"B");
        });
        NSLog(@"A");
    });
    // 结果是:BA (并发队列不会死锁) 并行队列,任务A加入队列执行中,然后任务B加入队列也立即执行,但是任务A会等任务B先执行完。

你可能感兴趣的:(OC-多线程GCD)