与 `dispatch_barrier` 的死锁
Dead Lock With `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_sync
或dispatch_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
.
异步发生
随着学习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_sync
或dispatch_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
.