奇怪的死锁与`静态`功能与否

时间:2016-05-26 13:01:39

标签: ios multithreading grand-central-dispatch deadlock

我有两天时间研究与读写锁相关的东西。 我遇到了一个问题,因为GCD的虚拟线程限制:Dead Lock With `dispatch_barrier`

然后我尝试使用pthread_rwlock_t来实现rwlock。它就像文章的结尾一样正常运行。

但是当我想将pthread_rwlock_t的初始化代码移动到static函数时,就像下面的代码一样,我发现它再次进入死锁状态。

然后我思考了很长时间并进行调试,我发现一件奇怪的事情: 当我从dispatch_once函数移出init代码(也包含static)时。一切都好。它太奇怪了,我认为这不是因为GCD的虚拟线程限制。 @ originaluser2

#import <pthread.h>

#define THREAD_ASSERT_ON_ERROR(x_) do { \
_Pragma("clang diagnostic push"); \
_Pragma("clang diagnostic ignored \"-Wunused-variable\""); \
volatile int res = (x_); \
assert(res == 0); \
_Pragma("clang diagnostic pop"); \
} while (0)

static pthread_rwlock_t kRWLock(){
    static pthread_rwlock_t _rwlock;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        THREAD_ASSERT_ON_ERROR(pthread_rwlock_init(&_rwlock, NULL));
    });
    return _rwlock;
}

//#define WILLDEADLOCK  //define it will see the deadlock demo

- (void)test
{
    dispatch_queue_t queue =  dispatch_queue_create("com.test.testasync", DISPATCH_QUEUE_CONCURRENT);
    for (NSInteger i=0; i<5000; i++) {
        dispatch_async(queue, ^{
#ifdef WILLDEADLOCK
            pthread_rwlock_t rwlock = kRWLock();
#else
            static pthread_rwlock_t rwlock;
            static dispatch_once_t onceToken;
            dispatch_once(&onceToken, ^{
                THREAD_ASSERT_ON_ERROR(pthread_rwlock_init(&rwlock, NULL));
            });
#endif
            THREAD_ASSERT_ON_ERROR(pthread_rwlock_rdlock(&rwlock));
            NSLog(@"rlock1");
            NSLog(@"runlock1");
            THREAD_ASSERT_ON_ERROR(pthread_rwlock_unlock(&rwlock));

            if (i%100==0) {
                THREAD_ASSERT_ON_ERROR(pthread_rwlock_wrlock(&rwlock));
                NSLog(@"wlock1");
                NSLog(@"wunlock1");
                THREAD_ASSERT_ON_ERROR(pthread_rwlock_unlock(&rwlock));
            }
        });
    }

    dispatch_barrier_sync(queue, ^{});
    NSLog(@"completed");
}

1 个答案:

答案 0 :(得分:0)

注意:这个问题的作者不允许将答案作为答案发布,所以我已经为他做了。

这是一个愚蠢的问题。我忘了pthread_rwlock_tstruct,它会在return之后复制到新内存。

以下正确的代码:

#import <pthread.h>

#define THREAD_ASSERT_ON_ERROR(x_) do { \
_Pragma("clang diagnostic push"); \
_Pragma("clang diagnostic ignored \"-Wunused-variable\""); \
volatile int res = (x_); \
assert(res == 0); \
_Pragma("clang diagnostic pop"); \
} while (0)

static pthread_rwlock_t *kRWLock(){
    static pthread_rwlock_t _rwlock;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        THREAD_ASSERT_ON_ERROR(pthread_rwlock_init(&_rwlock, NULL));
    });
    return &_rwlock;
}

- (void)test
{
    dispatch_queue_t queue =  dispatch_queue_create("com.test.testasync", DISPATCH_QUEUE_CONCURRENT);
    for (NSInteger i=0; i<5000; i++) {
        dispatch_async(queue, ^{
            pthread_rwlock_t *rwlock = kRWLock();

            THREAD_ASSERT_ON_ERROR(pthread_rwlock_rdlock(rwlock));
            NSLog(@"rlock1");
            NSLog(@"runlock1");
            THREAD_ASSERT_ON_ERROR(pthread_rwlock_unlock(rwlock));

            if (i%100==0) {
                THREAD_ASSERT_ON_ERROR(pthread_rwlock_wrlock(rwlock));
                NSLog(@"wlock1");
                NSLog(@"wunlock1");
                THREAD_ASSERT_ON_ERROR(pthread_rwlock_unlock(rwlock));
            }
        });
    }

    dispatch_barrier_sync(queue, ^{});
    NSLog(@"completed");
}