dispatch_barrier_sync总是死锁

Tim*_*ddy 3 deadlock grand-central-dispatch

给出以下代码段:

#import <XCTest/XCTest.h>

@interface DispatchTests : XCTestCase {
    dispatch_queue_t _workQueue;
    dispatch_queue_t _readWriteQueue;
    int _value;
}
-(void)read;
-(void)write;
@end

@implementation DispatchTests

-(void)testDispatch {
    _workQueue = dispatch_queue_create("com.work", DISPATCH_QUEUE_CONCURRENT);
    _readWriteQueue = dispatch_queue_create("com.readwrite", DISPATCH_QUEUE_CONCURRENT);
    _value = 0;
    for(int i = 0; i < 100; i++) {
        dispatch_async(_workQueue, ^{
            if(arc4random() % 4 == 0) {
                [self write];
            } else {
                [self read];
            }
        });
    }
    XCTestExpectation* expectation = [self expectationWithDescription:@"dude"];
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        [expectation fulfill];
    });

    [self waitForExpectationsWithTimeout:6.0 handler:nil];
}

-(void)read {
    dispatch_sync(_readWriteQueue, ^{
        NSLog(@"read:%d", _value);
    });
}

-(void)write {
    dispatch_barrier_sync(_readWriteQueue, ^{
        _value++;
        NSLog(@"write:%d", _value);
    });
}

@end
Run Code Online (Sandbox Code Playgroud)

该测试的目的是查看是否可以使用a dispatch_barrier来管理读/写锁定。在此测试中,读取器和写入器都是同步的。当我使障碍异步时,该测试似乎可以正常工作,但是我想避免异步行为,因为这种实现是不平凡的。

我试图了解为什么该write方法会死锁。根据GCD文档:

当屏障块到达专用并发队列的前面时,不会立即执行。相反,队列将等待直到其当前正在执行的块完成执行。此时,队列将自己执行屏障块。在障碍块完成之前,不会执行任何在障碍块之后提交的块。

我对“当前正在执行的块”的含义感到困惑。

我的解释是这种情况:提交一堆读(x),然后写(y),然后读更多(z):

  • (x)执行
  • (y)等到(x)完成
  • (y)个区块(z)无法执行
  • (x)完成
  • (y)执行
  • (y)完成
  • (z)执行
  • (z)完成

Cou*_*per 5

好的,在实际测试之后:理论上,您的代码不会阻塞。

但是-在实践中-它可能会

您遇到的情况是所有可用的系统线程都用尽了。为了继续进行,您的代码将要求GCD获取一个新线程-但现在不再可用-从而导致死锁。

为了避免这种情况,您需要分析代码以非绑定方式生成新线程的地方。这可能发生在并发队列中,在该队列中,该块将阻塞或花费太长时间才能完成,并且大量块以高频率提交给该并发队列。

例如,如果插入一个小的延迟:

for(int i = 0; i < 400; i++) {
    usleep(1000);
    dispatch_async(_workQueue, ^{
        if(arc4random() % 4 == 0) {
            [self write];
        } else {
            [self read];
        }

    });
}
Run Code Online (Sandbox Code Playgroud)

该代码可能会一直运行到正常完成为止。当然,这只是为了演示问题-不能解决您的问题。

  • 谢谢你的提示!我现在使用`dispatch_semaphore`限制线程数。 (2认同)