iOS中的鎖(5)
本文主要介紹pthread和讀寫鎖,以及自定義一個簡單的讀寫鎖。
1. pthread
1.1 TLS
安全傳輸層協(xié)議(TLS)用于在兩個通信應(yīng)用程序之間提供保密性和數(shù)據(jù)完整性。
該協(xié)議由兩層組成:
TLS 記錄協(xié)議(TLS Record)和 TLS 握手協(xié)議(TLS Handshake)。
傳輸層安全性協(xié)議(Transport Layer Security),縮寫為TLS,及其前身安全套接字(Secure Sockets Layer,縮寫作SSL)是一種安全協(xié)議,目的是為互聯(lián)網(wǎng)通信提供安全及數(shù)據(jù)完整性保障。網(wǎng)景公司(Netscape)在1994年推出首版網(wǎng)頁瀏覽器,網(wǎng)景導(dǎo)航者時,推出HTTP協(xié)議,以SSL進(jìn)行加密,這是SSL的起源。IETF將SSL進(jìn)行標(biāo)準(zhǔn)化,1999年公布第一版TLS標(biāo)準(zhǔn)文件,隨后又公布了RFC5246(2008年8月)與RFC6176(2011年3月)。在瀏覽器、郵箱、即時通訊、VoIP、網(wǎng)絡(luò)傳值等應(yīng)用程序中,廣泛支持這個協(xié)議。主要的網(wǎng)站,如Google、Facebook等也以這個協(xié)議來創(chuàng)建安全連線,發(fā)送數(shù)據(jù)。目前已成為互聯(lián)網(wǎng)上保密同學(xué)的工業(yè)標(biāo)準(zhǔn)。
SSL包含記錄層(Record Layer)和傳輸層,記錄層協(xié)議確定傳輸層數(shù)據(jù)的封裝格式。傳輸層安全協(xié)議使用X.509認(rèn)證,之后利用非對稱加密演算來對通信方做身份認(rèn)證,之后交換對稱密鑰作為會談密鑰(Session Key)。這會談密鑰是用來將通信兩方交換的數(shù)據(jù)做加密,保證兩個應(yīng)用間通信的保密性和可靠性,是客戶與服務(wù)器應(yīng)用之間的通信不被攻擊者竊聽。
1.2 TLS 線程相關(guān)解釋
線程局部存儲(Thread Local Storage,TLS): 是操作系統(tǒng)為線
程單獨提供的私有空間,通常只有有限的容量。Linux系統(tǒng)下
通常通過pthread庫中的 pthread_key_create()、pthread_getspecific()、 pthread_setspecific()、 pthread_key_delete()。
1.3 互斥鎖
在Posix Thread中定義有一套專?用于線程同步的mutex函數(shù)。mutex用于保證在任何時刻,都只能有一個線程訪問該對象。 當(dāng)獲取鎖操作失敗時,線程會進(jìn)入睡眠,等待鎖釋放時被喚醒。
- 創(chuàng)建和銷毀
-
POSIX定義了一個宏PTHREAD_MUTEX_INITIALIZER來靜態(tài)初始化互斥鎖 - 初始化:
int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *mutexattr) - 注銷:
pthread_mutex_destroy()
-
- 互斥鎖屬性
- 互斥鎖,分為遞歸鎖和非遞歸鎖。
- 對于遞歸鎖使用的是
PTHREAD_MUTEX_RECURSIVE
- 鎖操作
- 加鎖:
int pthread_mutex_lock(pthread_mutex_t *mutex) - 解鎖:
int pthread_mutex_unlock(pthread_mutex_t *mutex) - 嘗試加鎖:
int pthread_mutex_trylock(pthread_mutex_t *mutex),在鎖被占用是返回EBUSY,不會掛起線程等待。
- 加鎖:
2. 讀寫鎖
讀寫鎖實際是一種特殊的自旋鎖,它把對共享資源的訪問者劃分成讀者和寫者,讀者只對共享資源 進(jìn)行讀訪問,寫者則需要對共享資源進(jìn)行寫操作。這種鎖相對于自旋鎖而言,能提高并發(fā)性,因為在多處理器系統(tǒng)中,它允許同時有多個讀者來訪問共享資源,最大可能的讀者數(shù)為實際的邏輯CPU數(shù)。寫者是排他性的,一個讀寫鎖同時只能有一個寫者或多個讀者(與CPU數(shù)相關(guān)),但不能同時既有讀者又有寫者。在讀寫鎖保持期間也是搶占失效的。
如果讀寫鎖當(dāng)前沒有讀者,也沒有寫者,那么寫者可以立刻獲得讀寫鎖,否則它必須自旋在那里,直到?jīng)]有任何寫者或讀者。
如果讀寫鎖沒有寫者,那么讀者可以立即獲得該讀寫鎖,否則讀者必須自旋在那里,直到寫者釋放該讀寫鎖。
一次只有一個線程可以占有寫模式的讀寫鎖,但是可以有多個線程同時占有讀模式的讀寫鎖。正是因為這個特性,當(dāng)讀寫鎖是寫加鎖狀態(tài)時,在這個鎖被解鎖之前,所有試圖對這個鎖加鎖的線程都會被阻塞。當(dāng)讀寫鎖在讀加鎖狀態(tài)時,所有試圖以讀模式對它進(jìn)行加鎖的線程都可以得到訪問權(quán),但是如果線程希望以寫模式對此鎖進(jìn)行加鎖,它必須直到所有的線程釋放鎖。通常當(dāng)讀寫鎖處于讀模式鎖住狀態(tài)時,如果有另外線程試圖以寫模式加鎖,讀寫鎖通常會阻塞隨后的讀模式鎖請求,這樣可以避免讀模式鎖?期占用,而等待的寫模式鎖請求?期阻塞。讀寫鎖適合于對數(shù)據(jù)結(jié)構(gòu)的讀次數(shù)比寫次數(shù)多得多的情況。因為,讀模式鎖定時可以共享,以寫模式鎖住時意味著獨占, 所以讀寫鎖又叫共享-獨占鎖。
2.1 iOS中的的讀寫鎖
讀寫鎖適合于對數(shù)據(jù)結(jié)構(gòu)的讀次數(shù)比寫次數(shù)多得多的情況。因為,讀模式鎖定時可以共享,以寫模式鎖住時意味著獨占,所以讀寫鎖又叫共享-獨占鎖。
2.1.1 初始化讀寫鎖
#include <pthread.h>
int pthread_rwlock_init(pthread_rwlock_t *restrict rwlock, const pthread_rwlockattr_t *restrict attr);
int pthread_rwlock_destroy(pthread_rwlock_t *rwlock)
- 初始化成功則返回0, 出錯則返回錯誤編號。
- 同互斥量以上,在釋放讀寫鎖占用的內(nèi)存之前,需要先通過
pthread_rwlock_destroy對讀寫鎖進(jìn)行清理工作,釋放由init分配的資源。
2.1.2 讀寫鎖操作
- 讀鎖:
int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock); - 寫鎖:
int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock); - 解鎖:
int pthread_rwlock_unlock(pthread_rwlock_t *rwlock);
- 成功則返回0,出錯則返回錯誤編號。
- 對于讀鎖和寫鎖是會阻塞線程的
- 嘗試讀鎖:
int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock); - 嘗試寫鎖:
int pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock);
- 成功則返回0,出錯則返回錯誤編號。
- 非阻塞的獲取鎖操作,如果可以獲取則返回0,否則返回錯誤的EBUSY。
2.2 自定義讀寫鎖
2.2.1 自定義讀寫鎖代碼
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface MYRWLock : NSObject
// 讀數(shù)據(jù)
- (id)my_objectForKey:(NSString *)key;
// 寫數(shù)據(jù)
- (void)my_setObject:(id)obj forKey:(NSString *)key;
@end
NS_ASSUME_NONNULL_END
#import "MYRWLock.h"
@interface MYRWLock ()
// 定義一個并發(fā)隊列:
@property (nonatomic, strong) dispatch_queue_t concurrent_queue;
// 用戶數(shù)據(jù)中心, 可能多個線程需要數(shù)據(jù)訪問:
@property (nonatomic, strong) NSMutableDictionary *dataCenterDic;
@end
@implementation MYRWLock
- (id)init{
self = [super init];
if (self){
// 創(chuàng)建一個并發(fā)隊列:
self.concurrent_queue = dispatch_queue_create("com.my.read_write_queue", DISPATCH_QUEUE_CONCURRENT);
// 創(chuàng)建數(shù)據(jù)字典:
self.dataCenterDic = [NSMutableDictionary dictionary];
}
return self;
}
#pragma mark - 讀數(shù)據(jù)
- (id)my_objectForKey:(NSString *)key{
__block id obj;
// 同步讀取指定數(shù)據(jù):
dispatch_sync(self.concurrent_queue, ^{
obj = [self.dataCenterDic objectForKey:key];
});
return obj;
}
#pragma mark - 寫數(shù)據(jù)
- (void)my_setObject:(id)obj forKey:(NSString *)key{
// 異步柵欄調(diào)用設(shè)置數(shù)據(jù):
dispatch_barrier_async(self.concurrent_queue, ^{
[self.dataCenterDic setObject:obj forKey:key];
});
}
@end
這里在寫操作的時候,使用柵欄函數(shù)進(jìn)行阻塞,讀的時候不做限制。
2.2.2 使用示例
@property (nonatomic, strong) MYRWLock *rwLock;
- (void)testRWLock {
self.rwLock = [[MYRWLock alloc] init];
dispatch_queue_t queue = dispatch_queue_create("com.test.rwlock", DISPATCH_QUEUE_CONCURRENT);
for (int i = 0; i<10; i++) {
dispatch_async(queue, ^{
NSString *str = [NSString stringWithFormat:@"rw_test%d",i];
NSLog(@"寫%@---%@",str, [NSThread currentThread]);
[self.rwLock my_setObject:str forKey:@"rwKey"];
});
}
for (int i = 0; i<10; i++) {
dispatch_async(queue, ^{
NSLog(@"讀 %@---%@",[self.rwLock my_objectForKey:@"rwKey"], [NSThread currentThread]);
});
}
}