dispatch_sync() 的秘密!它一定会造成死锁吗?

dispatch_sync() 的秘密!它一定会造成死锁吗?

平时我们可能很少用到 dispatch_sync ,只是知道,如果调用 dispatch_sync(dispatch_get_main_queue(), block) 的话,会阻塞主线程。但事实并非如此。
另外,dispatch_sync() 和 dispatch_async() 可不仅仅是差了一个字母,也不仅仅是同步和异步的差别。

在看之前,建议首先要明确,队列、线程、任务,的关系。我发现很多人会把队列和线程搞混。一定要注意!

首先,dispatch_sync 的作用是,往某队列(第一个参数)中,添加一个同步任务(第二个参数)。

那么我们通常说其会造成主线程死锁的情况,比如场景0会造成死锁的情况。这大家都知道,就不多说了。

为了方便下面的讲解,先贴上官方文档的一个解释:
As an optimization, dispatch_sync() invokes the workitem on the thread which
submitted the workitem, except when the passed queue is the main queue or a queue targetting it (See dispatch_queue_main_t, dispatch_set_target_queue()).

作为优化,dispatch_sync() 执行它的任务时,会优先选择添加这个任务的线程,除非第一个参数你传入了主队列。

下面举例说明:
(PS: queue 和 queue2 是 serial 还是 concurrent 对本例影响不大)


场景0:

 1. (void)viewDidLoad {
    [super viewDidLoad];
    dispatch_sync(dispatch_get_main_queue(), ^{
    	NSLog(@"同步任务在 %@ 执行", [NSThread currentThread]);
    });
}

在场景0中,是在主线程中,往主队列添加一个同步任务,结果就造成死锁,主线程被阻塞。是相互等待的情况。

  1. 主线程正在执行主队列的任务。
  2. 一个同步任务过来了
  3. 于是,主线程立即等待
  4. 同步任务在等主线程当前任务完成,然而主线程在等待这个同步任务执行。
  5. 于是就造成了相互等待的情况。

场景1:

- (void)viewDidLoad {
    [super viewDidLoad];
    dispatch_queue_t queue = dispatch_queue_create("com.gcd.serial", DISPATCH_QUEUE_SERIAL);
    dispatch_async(queue, ^{
        NSLog(@"异步任务在 %@ 执行", [NSThread currentThread]);
        dispatch_sync(queue, ^{
            NSLog(@"同步任务在 %@ 执行", [NSThread currentThread]);
        });
    });
}

在场景1中,首先是在主线程中,往 queue 添加一个异步任务;然后子线程中,又往queue添加一个同步任务,就和场景0的情况是一样的,会造成死锁。


场景2:

- (void)viewDidLoad {
    [super viewDidLoad];
    dispatch_queue_t queue = dispatch_queue_create("com.gcd.serial", DISPATCH_QUEUE_SERIAL);
    dispatch_queue_t queue2 = dispatch_queue_create("com.gcd.concurrent", DISPATCH_QUEUE_CONCURRENT);
    dispatch_async(queue, ^{
    	NSLog(@"任务", [NSThread currentThread]);
    	dispatch_sync(queue2, ^{
    		NSLog(@"同步任务%@", [NSThread currentThread]);
    	});
    });
}

在场景2中,首先,是在主线程中,往queue添加一个异步任务;然后在子线程中,往queue2添加一个同步任务。

这种情况不会死锁,可以正常运行。

最终,这两个任务都会在同一个线程中执行,假如第一个 NSLog 打印线程 number = 3,那么第二个NSLog打印的也一定是3,因为会优先选择添加这个任务的线程


场景3:

- (void)viewDidLoad {
    [super viewDidLoad];
    dispatch_queue_t queue = dispatch_queue_create("com.gcd.serial", DISPATCH_QUEUE_SERIAL);
    dispatch_async(queue, ^{
    	NSLog(@"任务", [NSThread currentThread]);
    	//这里要注意了,并不会死锁!
    	dispatch_sync(dispatch_get_main_queue(), ^{
    	    NSLog(@"同步任务%@", [NSThread currentThread]);
    	});
    });
}

首先,是在主线程中,往queue添加一个异步任务;然后在子线程中,往主队列添加一个同步任务。

这种情况不会死锁,可以正常运行。

最终,第一个任务在子线程(比如number = 3)执行 ;第二个任务在主线程中执行,官方文档解释:除非第一个参数你传入了主队列


总结

  • 队列中的任务,最终会由线程调度,GCD管理线程的生命周期。
  • dispatch_sync() 是线程同步操作,其作用就是阻塞线程,先让它的block执行完毕,才会返回。
  • dispatch_sync() 造成死锁的条件是:如果在同一个队列(不管是不是主队列)添加同步任务,就会死锁,反之则不会。
- (void)viewDidLoad {
    [super viewDidLoad];
    //队列1
    dispatch_queue_t queue = dispatch_queue_create("com.gcd.serial", DISPATCH_QUEUE_SERIAL);
    //队列2
    dispatch_queue_t queue2 = dispatch_queue_create("com.gcd.concurrent", DISPATCH_QUEUE_CONCURRENT);
    //主线程中,往 queue 添加一个异步任务,是异步任务,不会等待。
    dispatch_async(queue, ^{
    	NSLog(@"异步任务在 %@" 执行, [NSThread currentThread]);
    	//这里是子线程,比如 number = 3
    	
    	//在子线程(number = 3),往 queue2 添加一个同步任务,此时子线程(number = 3)会立即等待,即被阻塞,等待block执行完毕
	//添加一个同步任务到queue2中
	dispatch_sync(queue2, ^{
	    //这个任务也会在number = 3 的子线程中执行
	    NSLog(@"同步任务在 %@ 执行", [NSThread currentThread]);
	});
		
	//同步任务执行完毕,继续执行异步任务
	NSLog(@"异步任务完成!")
    });
}

你可能感兴趣的:(日用而不知的常识,开发小心得)