面试题:信号量&死锁

1,182 阅读3分钟

1,信号量

1.1,什么事信号量-dispatch_semaphore_t?

dispatch_semaphore_t dispatch_semaphore_create(long value);

关于这个函数,我们在官网,查看他的定义是什么?

参数:信号量的初始值,不要小于0

返回值:就是创建了一个信号量

1.2,讨论

Passing zero for the value is useful for when two threads need to reconcile 
the completion of a particular event. Passing a value greater than zero is
 useful for managing a finite pool of resources, where the pool size is equal 
to the value.

翻译:
当两个线程需要协调特定事件的完成时,传递零是很有用的。
传递大于零的值对于管理有限的资源池非常有用,其中池大小等于该值。

1.3,注意点

Calls to dispatch_semaphore_signal must be balanced with calls to wait().
 Attempting to dispose of a semaphore with a count lower than value 
causes an EXC_BAD_INSTRUCTION exception.

翻译:
对dispatch_semaphore_signal的调用必须与对wait()的调用进行平衡。
试图以低于值的计数来处理信号量会导致EXC_BAD_INSTRUCTION异常。
也就是说 "dispatch_semaphore_signal""dispatch_semaphore_wait"必须一一匹配,
如果不匹配,就会报"EXC_BAD_INSTRUCTION"这个错误

1.4,使用

    dispatch_queue_t queue = dispatch_queue_create("CJL", DISPATCH_QUEUE_CONCURRENT);
    //利用信号量来改写
    dispatch_semaphore_t sem = dispatch_semaphore_create(0);
    for (int i = 0; i < 10; i++) {
        dispatch_async(queue, ^{
           NSLog(@"当前 - %d, 线程 - %@", i, [NSThread currentThread]);

            dispatch_semaphore_signal(sem);
        });
        dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);

    }
   /**
     * dispatch_semaphore_create(0)
     * dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER)
     * 答案:从0到9,按照顺序执行。因为需要等待一个信号回来,才执行下一个
     */
  • 异步函数+并发队列+信号量(0)
  • 从0到9,按照顺序执行。因为需要等待一个信号回来,才执行下一个

1.5,总结

  •  应用场景:同步当锁, 控制GCD最大并发数

  •  dispatch_semaphore_create():创建信号量

  • dispatch_semaphore_wait():等待信号量,信号量减1。当信号量< 0时会阻塞当前线程,根据传入的等待时间决定接下来的操作——如果永久等待将等到信号(signal)才执行下去

  • dispatch_semaphore_signal():释放信号量,信号量加1。当信号量>= 0 会执行wait之后的代码

面试题

1,异步函数+并发队列+信号量(1)+ dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER)

    dispatch_queue_t queue = dispatch_queue_create("CJL", DISPATCH_QUEUE_CONCURRENT);
    //利用信号量来改写
    dispatch_semaphore_t sem = dispatch_semaphore_create(1);
    for (int i = 0; i < 10; i++) {
        dispatch_async(queue, ^{
            NSLog(@"当前 - %d, 线程 - %@", i, [NSThread currentThread]);
            dispatch_semaphore_signal(sem);
        });
        dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
    }

  • dispatch_semaphore_create(1) & dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER)

  • 从0到9乱序执行,有时顺序执行,有时乱序执行

  • 因为控制信号量为1,又是异步函数,所以谁先发出signal信号不确定,造成有时顺序执行,有时乱序执行的情况

2,异步函数+并发队列+信号量(1)+ dispatch_semaphore_wait(sem, DISPATCH_TIME_NOW)

    dispatch_queue_t queue = dispatch_queue_create("CJL", DISPATCH_QUEUE_CONCURRENT);
    //利用信号量来改写
    dispatch_semaphore_t sem = dispatch_semaphore_create(1);
    for (int i = 0; i < 10; i++) {
        dispatch_async(queue, ^{
            NSLog(@"当前 - %d, 线程 - %@", i, [NSThread currentThread]);
            dispatch_semaphore_signal(sem);
        });
        dispatch_semaphore_wait(sem, DISPATCH_TIME_NOW);
    }

  • dispatch_semaphore_create(1) & dispatch_semaphore_wait(sem, DISPATCH_TIME_NOW)

  • 从0到9乱序执行,因为不用一直等待信号发出才执行,又是异步函数,所以乱序执行

3,异步函数+串行队列+信号量(1)或 (0)+ dispatch_semaphore_wait(sem, DISPATCH_TIME_NOW)

    dispatch_queue_t queue = dispatch_queue_create("CJL", DISPATCH_QUEUE_SERIAL_INACTIVE);
    NSLog(@"tt --- 1");
    //利用信号量来改写
    dispatch_semaphore_t sem = dispatch_semaphore_create(1);
    dispatch_async(queue, ^{
        NSLog(@"tt --- 4");
        dispatch_semaphore_signal(sem);
    });
    NSLog(@"tt --- 2");
    dispatch_semaphore_wait(sem, DISPATCH_TIME_NOW);
    NSLog(@"tt --- 3");
  • 异步函数+同步队列+信号量(1)或 (0)+ dispatch_semaphore_wait(sem, DISPATCH_TIME_NOW) 

  • 答案:1,2,3,崩溃

  • 因为是串行队列,加上信号量为1,相当于产生死锁

4,异步函数+串行队列+信号量(1)+ dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER)

    dispatch_queue_t queue = dispatch_queue_create("CJL", DISPATCH_QUEUE_SERIAL_INACTIVE);
    NSLog(@"tt --- 1");
    //利用信号量来改写
    dispatch_semaphore_t sem = dispatch_semaphore_create(1);
    dispatch_async(queue, ^{
        NSLog(@"tt --- 4");
        dispatch_semaphore_signal(sem);
    });
    NSLog(@"tt --- 2");
    dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
    NSLog(@"tt --- 3");
  • 答案:1,2,3,崩溃

5,异步函数+串行队列+信号量(0)+ dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER)

    dispatch_queue_t queue = dispatch_queue_create("CJL", DISPATCH_QUEUE_SERIAL_INACTIVE);
    NSLog(@"tt --- 1");
    //利用信号量来改写
    dispatch_semaphore_t sem = dispatch_semaphore_create(1);
    dispatch_async(queue, ^{
        NSLog(@"tt --- 4");
        dispatch_semaphore_signal(sem);
    });
    NSLog(@"tt --- 2");
    dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
    NSLog(@"tt --- 3");
  • 答案:1,2,阻塞

6,信号量(0)+ 主队列 + dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER)

    dispatch_semaphore_t sem = dispatch_semaphore_create(0);
    NSLog(@"dieLock2 ---- 1");
    dispatch_async(dispatch_get_main_queue(), ^{
        NSLog(@"dieLock2 ---- 4");
        dispatch_semaphore_signal(sem);
    }); 
   NSLog(@"dieLock2 ---- 2");
    dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
    NSLog(@"dieLock2 ---- 3");
    /*
     * 信号量为:dispatch_semaphore_create(0)
     * dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
     * 答案:1,2,等待
     * 为0时,因为不允许信号通过,所以为1,2,永久等待信号,才去执行
     */

7,信号量(0)或 (1)+ 主队列 + dispatch_semaphore_wait(sem, DISPATCH_TIME_NOW)

    dispatch_semaphore_t sem = dispatch_semaphore_create(0);
    NSLog(@"dieLock2 ---- 1");
    dispatch_async(dispatch_get_main_queue(), ^{
        NSLog(@"dieLock2 ---- 4");
        dispatch_semaphore_signal(sem);
    });
    NSLog(@"dieLock2 ---- 2");
    dispatch_semaphore_wait(sem, DISPATCH_TIME_NOW);
    NSLog(@"dieLock2 ---- 3");
  • 答案:1,2,3,4
  • 因为是DISPATCH_TIME_NOW,所以先于3先执行,在执行4

2,死锁

队列的相互等待会造成死锁

2.1,主线程+主队列

    NSLog(@"dieLock4 --- 1");
    dispatch_sync(dispatch_get_main_queue(), ^{
        NSLog(@"dieLock4 --- 3");
    });
    NSLog(@"dieLock4 --- 2");
    //答案:1,崩溃

2.2,异步函数嵌套同步函数 + 串行队列(即同步队列)

        // 同步队列
        dispatch_queue_t queue = dispatch_queue_create("com.CJL.Queue", NULL);
        NSLog(@"dieLock3 --- 1");
        // 异步函数
        dispatch_async(queue, ^{
            NSLog(@"dieLock3 --- 2");
            // 同步函数
            dispatch_sync(queue, ^{
                NSLog(@"dieLock3 --- 3");
            });
            NSLog(@"dieLock3 --- 4");
        });
        NSLog(@"dieLock3 --- 5")
    //答案:1,5,2,崩溃