? 在多線程操作過程中,往往一個數據同時被多個線程讀寫,在這種情況下,如果沒有相應的機制對數據進行保護,就很可能會發生數據污染的的問題,給程序造成各種難以重現的潛在bug。
多線程安全中相關術語及概念(假設操作的是數據庫):
(1)臟讀
? 指當一個事務正在訪問數據,并且對數據進行了修改,而這種修改還沒有提交到數據庫中。這時,另外一個事務也訪問這個數據,然后使用了這個數據。因為這個數據是還沒有提交的數據,那么另外一個事務讀到的這個數據是臟數據,依據臟數據所做的操作可能是不正確的。
(2)不可重復讀
指在一個事務內,多次讀同一數據。在這個事務還沒有結束時,另外一個事務也訪問該同一數據。那么,在第一個事務中的兩次讀數據之間,由于第二個事務的修改,那么第一個事務兩次讀到的的數據可能是不一樣的。這樣就發生了在一個事務內兩次讀到的數據是不一樣的,因此稱為是不可重復讀。
(3)幻覺讀
指當事務不是獨立執行時發生的一種現象,例如第一個事務對一個表中的數據進行了修改,這種修改涉及到表中的全部數據行。同時,第二個事務也修改這個表中的數據,這種修改是向表中插入一行新數據。那么,以后就會發生操作第一個事務的用戶發現表中還有沒有修改的數據行,就好象發生了幻覺一樣。例如:
目前工資為5000的員工有10人,事務A讀取所有工資為5000的人數為10人。此時,事務B插入一條工資也為5000的記錄。這時,事務A再次讀取工資為5000的員工,記錄為11人。此時產生了幻讀。
多線程的安全問題
線程不安全:就是不提供數據訪問保護,有可能出現多個線程先后更改數據造成所得到的數據是臟數據。當多個線程訪問同一塊資源時,很容易引發數據錯亂和數據安全問題。
線程安全:簡單來說就是多個線程同時對共享資源進行訪問時,采用了加鎖機制,當一個線程訪問共享資源,對該資源進行保護,其他線程不能進行訪問直到該線程讀取完,其他線程才可使用。
iOS多線程中的“鎖”
1、互斥鎖:@synchronized(id anObject)
-(void)myMethod:(id)anObj
{
@synchronized(anObj)
{
//do something here
}
}
2、atomic
OC在定義屬性時有nonatomic和atomic兩種選擇。
atomic:原子屬性,為setter方法加鎖(默認就是atomic)。
nonatomic:非原子屬性,不會為setter方法加鎖。
atomic加鎖原理:
@property(assign,atomic)intage;
-(void)setAge:(int)age
{
@synchronized(self){
_age=age;
}
}
3、NSLock
NSLock對象實現了NSLocking protocol,包含幾個方法:
lock——加鎖
unlock——解鎖
tryLock——嘗試加鎖,如果失敗了,并不會阻塞線程,只是立即返回NO
lockBeforeDate:——在指定的date之前暫時阻塞線程(如果沒有獲取鎖的話),如果到期還沒有獲取鎖,則線程被喚醒,函數立即返回NO。
比如:
NSLock *theLock=[[NSLockalloc]init];
if([theLocklock])
{
//do something here
[theLockunlock];
}
4、遞歸鎖:NSRecursiveLock
多次調用不會阻塞已獲取該鎖的線程。
NSRecursiveLock *rcsLock=[[NSRecursiveLockalloc]init];
voidrecursiveLockTest(intvalue)
{
[rcsLocklock];
if(value!=0)
{
--value;
recursiveLockTest(value);
}
[rcsLockunlock];
}
recursiveLockTest(5);
上面如果直接使用NSLock就會造成死鎖。NSRecursiveLock類定義的鎖可以在同一線程多次lock,而不會造成死鎖。遞歸鎖會跟蹤它被多少次lock。每次成功的lock都必須平衡調用unlock操作。只有所有的鎖住和解鎖操作都平衡的時候,鎖才真正被釋放給其他線程獲得。
5、條件鎖:NSConditionLock
有時一把只會lock和unlock的鎖未必就能完全滿足我們的使用。因為普通的鎖只能關心鎖與不鎖,而不在乎用什么鑰匙才能開鎖,而我們在處理資源共享的時候,多數情況是只有滿足一定條件的情況下才能打開這把鎖:
//主線程中
NSConditionLock *theLock=[[NSConditionLockalloc]init];
//線程1
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0),^{
for(inti=0;i<=2;i++)
{
[theLocklock];
NSLog(@"thread1:%d",i);
sleep(2);
[theLockunlockWithCondition:i];
}
});
//線程2
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0),^{
[theLocklockWhenCondition:2];
NSLog(@"thread2");
[theLockunlock];
});
在線程1中的加鎖使用了lock,是不需要條件的,所以順利的就鎖住了。但在unlock的使用了一個整型的條件,它可以開啟其它線程中正在等待這把鑰匙的臨界地,而線程2則需要一把被標識為2的鑰匙,所以當線程1循環到最后一次的時候,才最終打開了線程2中的阻塞。但即便如此,NSConditionLock也跟其它的鎖一樣,是需要lock與unlock對應的,只是lock、lockWhenCondition:與unlock,unlockWithCondition:是可以隨意組合的,當然這是與你的需求相關的。
6、分布鎖:NSDistributedLock
以上所有的鎖都是在解決多線程之間的沖突,但如果遇上多個進程或多個程序之間需要構建互斥的情景該怎么辦呢?這個時候我們就需要使用到NSDistributedLock了,從它的類名就知道這是一個分布式的Lock,NSDistributedLock的實現是通過文件系統的,所以使用它才可以有效的實現不同進程之間的互斥,但NSDistributedLock并非繼承于NSLock,它沒有lock方法,它只實現了tryLock,unlock,breakLock,所以如果需要lock的話,你就必須自己實現一個tryLock的輪詢。
7、GCD中信號量:dispatch_semaphore
假設現在系統有兩個空閑資源可以被利用,但同一時間卻有三個線程要進行訪問,這種情況下,該如何處理呢?這里,我們就可以方便的利用信號量來解決這個問題。同樣我們也可以用它來構建一把”鎖”(從本質上講,信號量與鎖是有區別的,具體的請自行查閱資料)。
信號量:就是一種可用來控制訪問資源的數量的標識。設定了一個信號量,在線程訪問之前,加上信號量的處理,則可告知系統按照我們指定的信號量數量來執行多個線程。
在GCD中有三個函數是semaphore的操作:
dispatch_semaphore_create創建一個semaphore
dispatch_semaphore_signal發送一個信號
dispatch_semaphore_wait等待信號
dispatch_semaphore_create函數有一個整形的參數,我們可以理解為信號的總量,dispatch_semaphore_signal是發送一個信號,自然會讓信號總量+1,dispatch_semaphore_wait等待信號,當信號總量少于0的時候就會一直等待,否則就可以正常的執行,并讓信號總量-1,根據這樣的原理,我們便可以快速的創建一個并發控制來同步任務和有限資源訪問控制。
8、GCD中“柵欄函數”:dispatch_barrier_async
dispatch_barrier_async函數的作用與barrier的意思相同,在進程管理中起到一個柵欄的作用,它等待所有位于barrier函數之前的操作執行完畢后執行,并且在barrier函數執行之后,barrier函數之后的操作才會得到執行,該函數需要同dispatch_queue_create函數生成的concurrent Dispatch Queue隊列一起使用。
dispatch_barrier_async函數的作用:
(1)實現高效率的數據庫訪問和文件訪問
(2)避免數據競爭
自旋鎖
????//?自旋鎖,性能最高,但已被證明不再安全
???{
????????OSSpinLock?lock?=?OS_SPINLOCK_INIT;
????????for(inti?=?0;?i?<?count;?i++)?{
????????????OSSpinLockLock(&lock);
????????????//?待加鎖的代碼
????????????OSSpinLockUnlock(&lock);
????????}
????}
信號量鎖
? ? //?信號量鎖,性能較高
????{
????????dispatch_semaphore_t?lock?=??dispatch_semaphore_create(1);
????????for(inti?=?0;?i?<?count;?i++)?{
????????????dispatch_semaphore_wait(lock,?DISPATCH_TIME_FOREVER);
????????????//?待加鎖的代碼
????????????dispatch_semaphore_signal(lock);
????????}
????}
互斥鎖
????//?互斥鎖,性能較高
????{
????????pthread_mutex_t?lock;
????????pthread_mutex_init(&lock,?NULL);
????????for(inti?=?0;?i?<?count;?i++)?{
????????????pthread_mutex_lock(&lock);
????????????//?待加鎖的代碼
????????????pthread_mutex_unlock(&lock);
????????}
????}
條件鎖
????//?條件鎖
????{
????????NSCondition?*lock?=?[NSCondition?new];
????????for(inti?=?0;?i?<?count;?i++)?{
????????????[lock?lock];
????????????//?待加鎖的代碼
????????????[lock?unlock];
????????}
????}
普通鎖
????//?普通鎖
????{
????????NSLock?*lock?=?[NSLock?new];
????????for(inti?=?0;?i?<?count;?i++)?{
????????????[lock?lock];
????????????//?待加鎖的代碼
????????????[lock?unlock];
????????}
????}
遞歸鎖
????//?遞歸鎖
????{
????????NSRecursiveLock?*lock?=?[NSRecursiveLock?new];
????????for(inti?=?0;?i?<?count;?i++)?{
????????????[lock?lock];
????????????//?待加鎖的代碼
????????????[lock?unlock];
????????}
????}
條件鎖
????//?條件鎖
????{
????????NSConditionLock?*lock?=?[[NSConditionLock?alloc]?initWithCondition:1];
????????for(inti?=?0;?i?<?count;?i++)?{
????????????[lock?lock];
????????????//?待加鎖的代碼
????????????[lock?unlock];
????????}
????}
便利鎖(自己YY的名)
????//?普通鎖,性能最差
????{
????????NSObject?*lock?=?[NSObject?new];
????????for(inti?=?0;?i?<?count;?i++)?{
????????????@synchronized(lock)?{
???????????????????//?待加鎖的代碼
????????????}
????????}
????}