dispatch_barrier_sync总是死锁 [英] dispatch_barrier_sync always deadlocks

查看:186
本文介绍了dispatch_barrier_sync总是死锁的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

给出以下代码段:

#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

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

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.

我试图理解为什么 write 方法陷入僵局。根据GCD文档:

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.

我被什么弄糊涂了

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

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


  • (x)执行

  • (y)等待(x)完成

  • (y)阻止执行(z)

  • (x)完成

  • (y)执行

  • (y)完成

  • (z)执行

  • (z)完成

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

推荐答案

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

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

但是-实际上-它可能

However - in practice - it may.

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

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.

这篇关于dispatch_barrier_sync总是死锁的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆