2016-05-26 5 views
0

У меня есть два дня, чтобы исследовать связанные с записью-записью блокировки. И я столкнулся с проблемой из-за ограничения виртуальной нити GCD: Dead Lock With `dispatch_barrier`Странный тупик с функцией `static` или нет

Затем я попытаюсь использовать pthread_rwlock_t для реализации rwlock. Он работает нормально, как конец статьи.

Но, когда я хочу, чтобы переместить код инициализации pthread_rwlock_t в static функции, как код ниже, я нашел, что это ввести затор снова.

После этого я думаю, что долгое время и отладки, я нахожу странную вещь: Когда я выхожу из кода инициализации (также содержит dispatch_once) от 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"); 
} 
+0

Когда вы отвечаете на свой вопрос здесь, введите его в качестве фактического ответа. Через пару дней вы можете «принять» свой ответ, чтобы люди знали, что он решен. – Almo

+0

@Almo Извините, я не был уполномочен писать ответы по некоторым причинам .. поэтому я должен обновить ответ в сообщении. – molon

+0

Хорошо, я отправлю его в качестве ответа для вас. :) – Almo

ответ

0

Примечание: Автор этого вопроса не было разрешено оставлять свой ответ как ответ, так что я сделал это для него.


Это глупый вопрос. Я забыл, что pthread_rwlock_t - это struct, и он будет скопирован в новую память после 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"); 
}