OC-多线程GCD

作者: 啊哈呵 | 来源:发表于2017-09-01 21:38 被阅读111次

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

    任务执行方式

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

    任务管理方式

    • 串行队列:所有任务会在一条线程中执行(有可能是当前线程也有可能是新开辟的线程),并且一个任务执行完毕后,才开始执行下一个任务。(等待完成)
    • 并行队列:可以开启多条线程并行执行任务(但不一定会开启新的线程),并且当一个任务放到指定线程开始执行时,下一个任务就可以开始执行了。(等待发生)
        // 主队列--串行,所有放在主队列中的任务,都会放到主线程中执行
        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队列就费了。

    任务+队列

    串行队列 并行队列 主队列
    同步(sync) 当前线程,串行执行 队列当前线程,串行执行 主新线程,串行执行(注意死锁)
    异步(async) 开1条新线程,串行执行 开n条新线程,异步执行(n在iphone7上面最大是几十个) 主新线程,串行执行

    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========<NSThread: 0x60800007f840>{number = 3, name = (null)}
         1========<NSThread: 0x60800007f840>{number = 3, name = (null)}
         2========<NSThread: 0x60800007f840>{number = 3, name = (null)}
         3========<NSThread: 0x60800007f840>{number = 3, name = (null)}
         4========<NSThread: 0x60800007f840>{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========<NSThread: 0x6000002616c0>{number = 1, name = main}
         4========<NSThread: 0x6000002616c0>{number = 1, name = main}
         1========<NSThread: 0x608000270540>{number = 3, name = (null)}
         2========<NSThread: 0x608000270540>{number = 3, name = (null)}
         3========<NSThread: 0x608000270540>{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========<NSThread: 0x60800007f840>{number = 3, name = (null)}
         1========<NSThread: 0x60800007f840>{number = 3, name = (null)}
         2========<NSThread: 0x60800007f840>{number = 3, name = (null)}
         3========<NSThread: 0x60800007f840>{number = 3, name = (null)}
         4========<NSThread: 0x60800007f840>{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========<NSThread: 0x608000070300>{number = 1, name = main}
         4========<NSThread: 0x608000070300>{number = 1, name = main}
         2========<NSThread: 0x608000264140>{number = 4, name = (null)}
         1========<NSThread: 0x60000007a800>{number = 3, name = (null)}
         3========<NSThread: 0x6080002642c0>{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========<NSThread: 0x60000026e000>{number = 3, name = (null)}
         4========<NSThread: 0x60000026e000>{number = 3, name = (null)}
         1========<NSThread: 0x60000007e2c0>{number = 1, name = main}
         2========<NSThread: 0x60000007e2c0>{number = 1, name = main}
         3========<NSThread: 0x60000007e2c0>{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========<NSThread: 0x600000263840>{number = 3, name = (null)}
         1========<NSThread: 0x608000078ec0>{number = 1, name = main}
         2========<NSThread: 0x608000078ec0>{number = 1, name = main}
         3========<NSThread: 0x608000078ec0>{number = 1, name = main}
         4========<NSThread: 0x600000263840>{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------<NSThread: 0x600000268a40>{number = 3, name = (null)}
         0------<NSThread: 0x600000268a40>{number = 3, name = (null)}
         1------<NSThread: 0x608000266e40>{number = 4, name = (null)}
         2------<NSThread: 0x608000266f00>{number = 5, name = (null)}
         3------<NSThread: 0x608000266f40>{number = 6, name = (null)}
         4------<NSThread: 0x600000268a40>{number = 3, name = (null)}
         5------<NSThread: 0x608000266e40>{number = 4, name = (null)}
         */
        
        
        /*
         同步队列:当前线程同步执行
         NSLogx信息
         CurrentThread------<NSThread: 0x608000072c00>{number = 3, name = (null)}
         0------<NSThread: 0x6000000694c0>{number = 1, name = main}
         1------<NSThread: 0x6000000694c0>{number = 1, name = main}
         2------<NSThread: 0x6000000694c0>{number = 1, name = main}
         3------<NSThread: 0x6000000694c0>{number = 1, name = main}
         4------<NSThread: 0x6000000694c0>{number = 1, name = main}
         5------<NSThread: 0x6000000694c0>{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信息
          当前线程=====<NSThread: 0x60000007c240>{number = 1, name = main}
          group-01 - <NSThread: 0x60800026d180>{number = 3, name = (null)}
          group-01 - <NSThread: 0x60800026d180>{number = 3, name = (null)}
          group-03 - <NSThread: 0x60000026c7c0>{number = 4, name = (null)}
          group-01 - <NSThread: 0x60800026d180>{number = 3, name = (null)}
          group-03 - <NSThread: 0x60000026c7c0>{number = 4, name = (null)}
          group-03 - <NSThread: 0x60000026c7c0>{number = 4, name = (null)}
          group-02 - <NSThread: 0x60000007c240>{number = 1, name = main}
          group-02 - <NSThread: 0x60000007c240>{number = 1, name = main}
          group-02 - <NSThread: 0x60000007c240>{number = 1, name = main}
          完成 - <NSThread: 0x60000007c240>{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
          完成 - <NSThread: 0x60800006e900>{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次_<NSThread: 0x608000268b00>{number = 4, name = (null)}
          第1次_<NSThread: 0x600000269240>{number = 6, name = (null)}
          第3次_<NSThread: 0x600000269100>{number = 5, name = (null)}
          第2次_<NSThread: 0x608000268ac0>{number = 3, name = (null)}
          第4次_<NSThread: 0x600000269780>{number = 7, name = (null)}
          第8次_<NSThread: 0x608000268ac0>{number = 3, name = (null)}
          第7次_<NSThread: 0x600000269100>{number = 5, name = (null)}
          第6次_<NSThread: 0x608000268b00>{number = 4, name = (null)}
          第5次_<NSThread: 0x600000269240>{number = 6, name = (null)}
          第9次_<NSThread: 0x600000269780>{number = 7, name = (null)}
          */
        
        /*
         dispatch_semaphore_t semaphore = dispatch_semaphore_create(10);
         10条线程
         NSLog信息:
         第2次_<NSThread: 0x6080000661c0>{number = 3, name = (null)}
         第4次_<NSThread: 0x608000073e40>{number = 7, name = (null)}
         第1次_<NSThread: 0x600000079dc0>{number = 4, name = (null)}
         第5次_<NSThread: 0x6000000721c0>{number = 8, name = (null)}
         第3次_<NSThread: 0x608000073dc0>{number = 6, name = (null)}
         第0次_<NSThread: 0x608000073d40>{number = 5, name = (null)}
         第6次_<NSThread: 0x608000073d80>{number = 9, name = (null)}
         第9次_<NSThread: 0x608000073e00>{number = 10, name = (null)}
         第7次_<NSThread: 0x6000000717c0>{number = 11, name = (null)}
         第8次_<NSThread: 0x600000066b40>{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<UITouch *> *)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========<NSThread: 0x60000007c5c0>{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========<NSThread: 0x6000000690c0>{number = 1, name = main}
         1========<NSThread: 0x6000000690c0>{number = 1, name = main}
         2========<NSThread: 0x6000000690c0>{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

        本文链接:https://www.haomeiwen.com/subject/ruwhjxtx.html