带有`dispatch_barrier`的死锁 [英] Dead Lock With `dispatch_barrier`

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

问题描述

通过学习dispatch_barrier,我在下面写了一个示例:

static dispatch_queue_t queue;
    static dispatch_queue_t readWriteLockQueue;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        queue = dispatch_queue_create("com.test.testasync", DISPATCH_QUEUE_CONCURRENT);
        readWriteLockQueue = dispatch_queue_create("com.test.readWriteLockQueueOfMeta", DISPATCH_QUEUE_CONCURRENT);
    });

    for (NSInteger i=0; i<100; i++) {
        dispatch_async(queue, ^{
            dispatch_sync(readWriteLockQueue, ^{
                NSLog(@"read");
            });

            dispatch_barrier_sync(readWriteLockQueue, ^{
                NSLog(@"write");
            });
        });
    }
    dispatch_barrier_async(queue, ^{
        NSLog(@"finished!");
    });

但是输出是:

2016-05-20 16:23:14.066 Test[23867:781553] read
2016-05-20 16:23:14.066 Test[23867:781555] read
2016-05-20 16:23:14.066 Test[23867:781554] read
2016-05-20 16:23:14.066 Test[23867:781545] read
2016-05-20 16:23:14.066 Test[23867:781559] read
2016-05-20 16:23:14.067 Test[23867:781564] read
2016-05-20 16:23:14.066 Test[23867:781560] read
2016-05-20 16:23:14.066 Test[23867:781561] read
2016-05-20 16:23:14.067 Test[23867:781562] read
2016-05-20 16:23:14.067 Test[23867:781565] read

没有出现write.超出了我的期望. 所以我需要一些帮助.

dispatch_barrier_syncdispatch_sync尚未在内部调用sync. 为什么会发生死锁?

已更新: 感谢@ originaluser2的回答. 我在下面用读写锁更新了我的工具,以避免虚拟线程限制.

#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 dispatch_queue_t queue;
    static dispatch_once_t onceToken;
    static pthread_rwlock_t rwlock;
    dispatch_once(&onceToken, ^{
        queue = dispatch_queue_create("com.test.testasync", DISPATCH_QUEUE_CONCURRENT);
        THREAD_ASSERT_ON_ERROR(pthread_rwlock_init(&rwlock, NULL));
    });

    NSMutableDictionary *dict = [NSMutableDictionary dictionary];

    for (NSInteger i=0; i<200; i++) {
        dispatch_async(queue, ^{
            NSString *result = nil;
            THREAD_ASSERT_ON_ERROR(pthread_rwlock_rdlock(&rwlock));
            result = dict[@"test"];
            THREAD_ASSERT_ON_ERROR(pthread_rwlock_unlock(&rwlock));

            if (!result) {
                THREAD_ASSERT_ON_ERROR(pthread_rwlock_wrlock(&rwlock));
                NSLog(@"%ld:try to write",i);
                result = dict[@"test"];
                if (!result) {
                    NSLog(@"%ld:write",i);
                    dict[@"test"] = @"result";
                    result = dict[@"test"];
                }
                THREAD_ASSERT_ON_ERROR(pthread_rwlock_unlock(&rwlock));
            }
            NSLog(@"%ld:%@",i,result);
        });
    }

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

解决方案

问题是您计划要同时执行100个任务.这将超出虚拟线程限制(通常为64个)–因此,您将有64个任务在那里等待它们的读取或写入完成,但是它们无法完成,因为没有更多的线程可以执行这些任务了. >

如果将循环减小到64,或者将queue设置为串行队列以使任务瓶颈,则代码将再次起作用.虽然,这是一个非常人为的示例.实际上,您永远不会同时进行如此多有争议的读写操作(这表明您的逻辑存在更根本的问题)–即使您这样做了,您的写入也很可能是与dispatch_barrier_async异步发生.

With learnning dispatch_barrier, I wrote a example below:

static dispatch_queue_t queue;
    static dispatch_queue_t readWriteLockQueue;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        queue = dispatch_queue_create("com.test.testasync", DISPATCH_QUEUE_CONCURRENT);
        readWriteLockQueue = dispatch_queue_create("com.test.readWriteLockQueueOfMeta", DISPATCH_QUEUE_CONCURRENT);
    });

    for (NSInteger i=0; i<100; i++) {
        dispatch_async(queue, ^{
            dispatch_sync(readWriteLockQueue, ^{
                NSLog(@"read");
            });

            dispatch_barrier_sync(readWriteLockQueue, ^{
                NSLog(@"write");
            });
        });
    }
    dispatch_barrier_async(queue, ^{
        NSLog(@"finished!");
    });

But the output is:

2016-05-20 16:23:14.066 Test[23867:781553] read
2016-05-20 16:23:14.066 Test[23867:781555] read
2016-05-20 16:23:14.066 Test[23867:781554] read
2016-05-20 16:23:14.066 Test[23867:781545] read
2016-05-20 16:23:14.066 Test[23867:781559] read
2016-05-20 16:23:14.067 Test[23867:781564] read
2016-05-20 16:23:14.066 Test[23867:781560] read
2016-05-20 16:23:14.066 Test[23867:781561] read
2016-05-20 16:23:14.067 Test[23867:781562] read
2016-05-20 16:23:14.067 Test[23867:781565] read

No write appeared. It exceeded my expectations. So I need some help about it.

dispatch_barrier_sync or dispatch_sync has not call sync inside. Why does it happened deadlock?

Updated: Thanks for the answer of @originaluser2. I updated my implement with read-write lock below to avoid virtual thread limit.

#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 dispatch_queue_t queue;
    static dispatch_once_t onceToken;
    static pthread_rwlock_t rwlock;
    dispatch_once(&onceToken, ^{
        queue = dispatch_queue_create("com.test.testasync", DISPATCH_QUEUE_CONCURRENT);
        THREAD_ASSERT_ON_ERROR(pthread_rwlock_init(&rwlock, NULL));
    });

    NSMutableDictionary *dict = [NSMutableDictionary dictionary];

    for (NSInteger i=0; i<200; i++) {
        dispatch_async(queue, ^{
            NSString *result = nil;
            THREAD_ASSERT_ON_ERROR(pthread_rwlock_rdlock(&rwlock));
            result = dict[@"test"];
            THREAD_ASSERT_ON_ERROR(pthread_rwlock_unlock(&rwlock));

            if (!result) {
                THREAD_ASSERT_ON_ERROR(pthread_rwlock_wrlock(&rwlock));
                NSLog(@"%ld:try to write",i);
                result = dict[@"test"];
                if (!result) {
                    NSLog(@"%ld:write",i);
                    dict[@"test"] = @"result";
                    result = dict[@"test"];
                }
                THREAD_ASSERT_ON_ERROR(pthread_rwlock_unlock(&rwlock));
            }
            NSLog(@"%ld:%@",i,result);
        });
    }

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

解决方案

The problem is that you're scheduling 100 tasks to be executed concurrently. This will exceed the virtual thread limit (usually 64) – and therefore you'll have 64 tasks sitting there waiting for their read or writes to finish, but they can't finish because there's no more threads left to do them on.

If you reduce your loop down to 64, or make your queue a serial queue in order to bottleneck the tasks, the code will once again work. Although, this is a pretty contrived example. In reality, you would never have so many contested read and writes happening concurrently (this would be an indication of a more fundamental problem in your logic) – and even if you did, your writes should most probably be happening asynchronously with a dispatch_barrier_async.

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

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