在上一篇文章中,我們主要了解了多線程安全隱患,以及它的解決方案。其中,我們有了解到自旋鎖和互斥鎖,這兩種經常用到的鎖。這里有一個大致的總結,看看這兩個鎖在什么情況下比較合適:
1、什么情況使用自旋鎖比較劃算?
預計線程等待鎖的時間很短
加鎖的代碼(臨界區(qū))經常被調用,但競爭情況很少發(fā)生
CPU資源不緊張
多核處理器
什么情況使用互斥鎖比較劃算?
2、預計線程等待鎖的時間較長
單核處理器
臨界區(qū)有IO操作
臨界區(qū)代碼復雜或者循環(huán)量大
臨界區(qū)競爭非常激烈
接下來,我們再看另一個知識點,是關于讀寫安全方面的知識,首先我們來認識下atomic這個修飾符,在iOS這個修飾符的作用就是保證屬性setter、getter的原子性操作,相當于在getter和setter內部加了線程同步的鎖,它能保證在單個讀和寫的過程中是安全的,但是它并不能保證使用屬性的過程是線程安全的。
這里,我們就針對這個使用屬性過程的線程安全問題來看下,首先,我們需要了解方案的具體要求。大致如下:
1、同一時間,只能有1個線程進行寫的操作。
2、同一時間,允許有多個線程進行讀的操作。
3、同一時間,不允許既有寫的操作,又有讀的操作。
總結一句話的意思,就是“多讀單寫”,就是可以多條線程同時讀取,但是在寫入的時候,只能有一條線程在操作這個屬性。iOS的常用實現方案有:
1、pthread_rwlock:讀寫鎖
2、dispatch_barrier_async:異步柵欄
先來看看C語言中的讀寫鎖pthread_rwlock的使用:
- (void)demo {
pthread_rwlock_init(&_lock, NULL);
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
for (int i = 0; i < 10; i++) {
dispatch_async(queue, ^{
[self read];
});
dispatch_async(queue, ^{
[self write];
});
}
}
- (void)read {
pthread_rwlock_rdlock(&_lock);
sleep(1);
NSLog(@"read");
pthread_rwlock_unlock(&_lock);
}
- (void)write
{
pthread_rwlock_wrlock(&_lock);
sleep(1);
NSLog(@"write");
pthread_rwlock_unlock(&_lock);
}
- (void)dealloc {
pthread_rwlock_destroy(&_lock);
}
打印結果如下圖:

我們可以很清楚的發(fā)現,只有在read操作的時候,是有多條線程同時打印的,任何write操作,都是會有1秒鐘的間隔。
接下來,我們來看看異步柵欄操作:
- (void)demo {
dispatch_queue_t queue = dispatch_queue_create("rw_queue", DISPATCH_QUEUE_CONCURRENT);
for (int i = 0; i < 10; i++) {
dispatch_async(queue, ^{
[self read];
});
dispatch_async(queue, ^{
[self read];
});
dispatch_async(queue, ^{
[self read];
});
dispatch_barrier_async(queue, ^{
[self write];
});
};
}
- (void)read {
sleep(1);
NSLog(@"read");
}
- (void)write
{
sleep(1);
NSLog(@"write");
}
打印結果如下:

也是我們所希望看到的結果。
不過這里有個必要的點,我們需要了解下,就是創(chuàng)建的queue必須是自定義的DISPATCH_QUEUE_CONCURRENT,如果是自定義串行隊列或者直接使用全局并發(fā)隊列,那么dispatch_barrier_async這個函數等同于dispatch_async!
以上就是這次對多線程知識點總結的補充內容!