dispatch_barrier_sync always deadlocks

痴心易碎 提交于 2019-12-06 06:23:56

问题


Given the following snippet of code:

#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

The purpose of this test is to see if I can use a dispatch_barrier to manage a read/write lock. In this test, both the reader and writer are synchronous. The test seems to work fine when I make the barrier asynchronous, however I would like to avoid asynchronous behavior because this implementation is non-trivial.

I'm trying to understand why the write method is deadlocking. According to the GCD docs:

When the barrier block reaches the front of a private concurrent queue, it is not executed immediately. Instead, the queue waits until its currently executing blocks finish executing. At that point, the queue executes the barrier block by itself. Any blocks submitted after the barrier block are not executed until the barrier block completes.

I'm confused by what is meant by "currently executing blocks".

My interpretation is this scenario where a bunch of reads (x) get submitted, then a write (y), then more reads (z):

  • (x) executes
  • (y) waits until (x) are done
  • (y) blocks (z) from executing
  • (x) completes
  • (y) executes
  • (y) completes
  • (z) executes
  • (z) completes

回答1:


OK, after actual testing it: your code does not block - in theory.

However - in practice - it may.

What you are experiencing is a situation where all available system threads are exhausted. In order to proceed, your code would require GCD to acquire a new thread - but non is available anymore - and thus, it deadlocks.

In order to avoid such situation, you need to analyse your code where it spawns new threads in an unbound manner. This may occur with concurrent queues, where the block will block or takes too long to finish AND a high number of blocks are submitted in high frequency to that concurrent queue.

For example, if you insert a small delay:

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

    });
}

the code may run until it finishes regularly. This of course is just to demonstrate the issue - not to fix your problem.



来源:https://stackoverflow.com/questions/36634315/dispatch-barrier-sync-always-deadlocks

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!