要用到多線程 ,就不得不考慮,線程之間的交互,線程是否安全安全
推薦一個原文連接 是關於 線程鎖的基本使用的 http://blog.csdn.net/qq_30513483/article/details/52349968
這篇博客,也主要引用 這個連接的內容多線程
4.1 OSSpinLock 自旋鎖 :併發
線程經過busy-wait-loop的方式來獲取鎖,任時刻只有一個線程可以得到鎖,其餘線程忙等待直到得到鎖。異步
spinlock在多處理器多線程環境的場景中有很普遍的使用,通常要求使用spinlock的臨界區儘可能簡短,這樣獲取的鎖能夠儘快釋放,以知足其餘忙等的線程。async
Spinlock和mutex不一樣,spinlock不會致使線程的狀態切換(用戶態->內核態),可是spinlock使用不當(如臨界區執行時間過長)會致使cpu busy飆高。函數
/*oop
OS_SPINLOCK_INIT: 默認值爲
0
,在locked
狀態時就會大於0
,unlocked
狀態下爲0
OSSpinLockLock(&oslock):上鎖,參數爲OSSpinLock
地址
OSSpinLockUnlock(&oslock):解鎖,參數爲OSSpinLock
地址
OSSpinLockTry(&oslock):嘗試加鎖,能夠加鎖則當即加鎖並返回YES
,反之返回NO
spa
這裏順便提一下trylock
和lock
使用場景:.net
當前線程鎖失敗,也能夠繼續其它任務,用 trylock 合適
當前線程只有鎖成功後,纔會作一些有意義的工做,那就 lock,不必輪詢 trylock線程
*/
使用:::導入 #import <libkern/OSAtomic.h> iOS 10.0 後被取消 ,須要更換這個鎖
- (void)osspinLock{
__block OSSpinLock oslock = OS_SPINLOCK_INIT;
//線程1
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSLog(@"線程1 準備上鎖 %@",[NSThread currentThread]);
OSSpinLockLock(&oslock);
sleep(4);
NSLog(@"線程1 代碼執行 %@",[NSThread currentThread]);
OSSpinLockUnlock(&oslock);
NSLog(@"線程1 解鎖成功 %@",[NSThread currentThread]);
NSLog(@"--------------------------------------------------------");
});
//線程2
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSLog(@"線程2 準備上鎖 %@",[NSThread currentThread]);
OSSpinLockLock(&oslock);
NSLog(@"線程2 代碼執行 %@",[NSThread currentThread]);
OSSpinLockUnlock(&oslock);
NSLog(@"線程2 解鎖成功 %@",[NSThread currentThread]);
}); }
執行結果:::
2017-12-21 15:24:11.495148+0800 DeadThread[12619:4480008] 線程1 準備上鎖 <NSThread: 0x608000071940>{number = 3, name = (null)}
2017-12-21 15:24:11.495148+0800 DeadThread[12619:4480007] 線程2 準備上鎖 <NSThread: 0x60c000261180>{number = 4, name = (null)}
2017-12-21 15:24:11.495376+0800 DeadThread[12619:4480007] 線程2 代碼執行 <NSThread: 0x60c000261180>{number = 4, name = (null)}
2017-12-21 15:24:11.495470+0800 DeadThread[12619:4480007] 線程2 解鎖成功 <NSThread: 0x60c000261180>{number = 4, name = (null)}
2017-12-21 15:24:15.498949+0800 DeadThread[12619:4480008] 線程1 代碼執行 <NSThread: 0x608000071940>{number = 3, name = (null)}
2017-12-21 15:24:15.499124+0800 DeadThread[12619:4480008] 線程1 解鎖成功 <NSThread: 0x608000071940>{number = 3, name = (null)}
2017-12-21 15:24:15.499216+0800 DeadThread[12619:4480008] --------------------------------------------------------
總結結果:::
1.兩個任務都要 異步 + 併發:因此會從新開啓兩個子線程;
2.兩個線程都有鎖:當鎖定後,會執行完這個任務,纔會執行下一個任務;
3.這個結果 只是一中狀況,異步併發 會先發起哪一個任務是不定的;
對上面的代碼稍做修改:::: 給 解鎖的代碼 添加註釋
- (void)osspinLock{ __block OSSpinLock oslock = OS_SPINLOCK_INIT; //線程1 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ NSLog(@"線程1 準備上鎖 %@",[NSThread currentThread]); OSSpinLockLock(&oslock); sleep(4); NSLog(@"線程1 代碼執行 %@",[NSThread currentThread]); // OSSpinLockUnlock(&oslock); NSLog(@"線程1 解鎖成功 %@",[NSThread currentThread]); NSLog(@"--------------------------------------------------------"); }); //線程2 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ NSLog(@"線程2 準備上鎖 %@",[NSThread currentThread]); OSSpinLockLock(&oslock); NSLog(@"線程2 代碼執行 %@",[NSThread currentThread]); // OSSpinLockUnlock(&oslock); NSLog(@"線程2 解鎖成功 %@",[NSThread currentThread]); }); }
執行結果::::
2017-12-21 15:29:34.099561+0800 DeadThread[12653:4494849] 線程1 準備上鎖 <NSThread: 0x60c000262100>{number = 3, name = (null)} 2017-12-21 15:29:34.099561+0800 DeadThread[12653:4494848] 線程2 準備上鎖 <NSThread: 0x60000047bf00>{number = 4, name = (null)} 2017-12-21 15:29:38.100529+0800 DeadThread[12653:4494849] 線程1 代碼執行 <NSThread: 0x60c000262100>{number = 3, name = (null)} 2017-12-21 15:29:38.100739+0800 DeadThread[12653:4494849] 線程1 解鎖成功 <NSThread: 0x60c000262100>{number = 3, name = (null)} 2017-12-21 15:29:38.100880+0800 DeadThread[12653:4494849] --------------------------------------------------------
總結結果:::
1.兩個任務,每一個都含有鎖:當有個任務先獲得執行的時候,任務執行完沒有了解鎖的過程
可是另外一個任務,會一直等待 鎖解除才能執行, 始終不會執行……
4.2 pthread_mutex 互斥鎖 ::
使用::#import <pthread.h>
/**
pthread_mutex 中也有個pthread_mutex_trylock(&pLock),
和上面提到的 OSSpinLockTry(&oslock)
區別在於,前者能夠加鎖時返回的是 0,不然返回一個錯誤提示碼;
後者返回的 YES和NO
*/
-(void)mutexLock{ static pthread_mutex_t pLock; pthread_mutex_init(&pLock, NULL); //1.線程1 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ NSLog(@"線程1 準備上鎖 %@",[NSThread currentThread]); pthread_mutex_lock(&pLock); sleep(3); NSLog(@"線程1 執行 %@",[NSThread currentThread]); pthread_mutex_unlock(&pLock); NSLog(@"線程1 成功解鎖 %@",[NSThread currentThread]); }); //1.線程2 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ NSLog(@"線程2 準備上鎖 %@",[NSThread currentThread]); pthread_mutex_lock(&pLock); sleep(3); NSLog(@"線程2 執行 %@",[NSThread currentThread]); pthread_mutex_unlock(&pLock); NSLog(@"線程2 成功解鎖 %@",[NSThread currentThread]); }); }
執行結果:::
2017-12-21 15:37:37.219816+0800 DeadThread[12685:4529083] 線程2 準備上鎖 <NSThread: 0x60000006c980>{number = 4, name = (null)} 2017-12-21 15:37:37.219816+0800 DeadThread[12685:4529086] 線程1 準備上鎖 <NSThread: 0x604000469880>{number = 3, name = (null)} 2017-12-21 15:37:40.223145+0800 DeadThread[12685:4529083] 線程2 執行 <NSThread: 0x60000006c980>{number = 4, name = (null)} 2017-12-21 15:37:40.223404+0800 DeadThread[12685:4529083] 線程2 成功解鎖 <NSThread: 0x60000006c980>{number = 4, name = (null)} 2017-12-21 15:37:43.226685+0800 DeadThread[12685:4529086] 線程1 執行 <NSThread: 0x604000469880>{number = 3, name = (null)} 2017-12-21 15:37:43.226809+0800 DeadThread[12685:4529086] 線程1 成功解鎖 <NSThread: 0x604000469880>{number = 3, name = (null)}
結果分析:::
1.異步 + 併發 -> 兩個線程;
2.先加鎖的任務 先完成,等待任務完成解鎖,在完成下一個任務;
4.3 pthread_mutex(recursive) 遞歸鎖
/**
通過上面幾種例子,咱們能夠發現:
加鎖後只能有一個線程訪問該對象,後面的線程須要排隊,
而且 lock 和 unlock 是對應出現的,同一線程屢次 lock 是不容許的,
而遞歸鎖容許同一個線程在未釋放其擁有的鎖時反覆對該鎖進行加鎖操做。
上面的代碼若是咱們用 pthread_mutex_init(&pLock, NULL) 初始化會出現死鎖的狀況,遞歸鎖能很好的避免這種狀況的死鎖;
*/
-(void)recursiveLock{ static pthread_mutex_t pLock; pthread_mutexattr_t attr; pthread_mutexattr_init(&attr); //初始化attr而且給它賦予默認 pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); //設置鎖類型,這邊是設置爲遞歸鎖 pthread_mutex_init(&pLock, &attr); pthread_mutexattr_destroy(&attr); //銷燬一個屬性對象,在從新進行初始化以前該結構不能從新使用 static void (^RecursiveBlock)(int); //1.線程1 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ RecursiveBlock = ^(int value) { pthread_mutex_lock(&pLock); if (value > 0) { NSLog(@"value: %d", value); RecursiveBlock(value - 1); } pthread_mutex_unlock(&pLock); }; RecursiveBlock(5); }); }
執行結果:::
2017-12-21 15:45:39.095417+0800 DeadThread[12721:4562499] value: 5 2017-12-21 15:45:39.095583+0800 DeadThread[12721:4562499] value: 4 2017-12-21 15:45:39.096599+0800 DeadThread[12721:4562499] value: 3 2017-12-21 15:45:39.097190+0800 DeadThread[12721:4562499] value: 2 2017-12-21 15:45:39.097645+0800 DeadThread[12721:4562499] value: 1
結果分析:::
1.開啓新的線程;
2.遞歸鎖 ->容許同一個線程在未釋放其擁有的鎖時反覆對該鎖進行加鎖操做;
4.4 dispatch_semaphore 信號量
/**
dispatch_semaphore_create(1): 傳入值必須
>=0
, 若傳入爲0
則阻塞線程並等待timeout,時間到後會執行其後的語句
dispatch_semaphore_wait(signal, overTime):能夠理解爲lock
,會使得signal
值-1
dispatch_semaphore_signal(signal):能夠理解爲unlock
,會使得signal
值+1
*/
-(void)GCDSemaphore{ dispatch_semaphore_t signal = dispatch_semaphore_create(1); //傳入值必須 >=0, 若傳入爲0則阻塞線程並等待timeout,時間到後會執行其後的語句 dispatch_time_t overTime = dispatch_time(DISPATCH_TIME_NOW, 3.0f * NSEC_PER_SEC); //線程1 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ NSLog(@"線程1 等待ing %@",[NSThread currentThread]); dispatch_semaphore_wait(signal, overTime); //signal 值 -1 sleep(4); NSLog(@"線程1 執行 %@",[NSThread currentThread]); dispatch_semaphore_signal(signal); //signal 值 +1 NSLog(@"線程1 發送信號 %@",[NSThread currentThread]); NSLog(@"--------------------------------------------------------"); }); //線程2 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ NSLog(@"線程2 等待ing %@",[NSThread currentThread]); dispatch_semaphore_wait(signal, overTime); sleep(4); NSLog(@"線程2 執行 %@",[NSThread currentThread]); dispatch_semaphore_signal(signal); NSLog(@"線程2 發送信號 %@",[NSThread currentThread]); }); }
執行結果:::
2017-12-21 15:49:55.355078+0800 DeadThread[12761:4592703] 線程1 等待ing <NSThread: 0x60c000076400>{number = 3, name = (null)} 2017-12-21 15:49:55.355079+0800 DeadThread[12761:4592705] 線程2 等待ing <NSThread: 0x60c00007d740>{number = 5, name = (null)} 2017-12-21 15:49:59.360317+0800 DeadThread[12761:4592703] 線程1 執行 <NSThread: 0x60c000076400>{number = 3, name = (null)} 2017-12-21 15:49:59.360482+0800 DeadThread[12761:4592703] 線程1 發送信號 <NSThread: 0x60c000076400>{number = 3, name = (null)} 2017-12-21 15:49:59.360568+0800 DeadThread[12761:4592703] -------------------------------------------------------- 2017-12-21 15:50:02.358932+0800 DeadThread[12761:4592705] 線程2 執行 <NSThread: 0x60c00007d740>{number = 5, name = (null)} 2017-12-21 15:50:02.359109+0800 DeadThread[12761:4592705] 線程2 發送信號 <NSThread: 0x60c00007d740>{number = 5, name = (null)}
結果分析:::
1.初始信號量大於0
能夠發現,由於咱們初始化信號量的時候是大於 0
的,因此並無阻塞線程,而是直接執行了 線程1 線程2。
把信號量設置爲 0 修改上面代碼
dispatch_semaphore_t signal = dispatch_semaphore_create(0);
執行結果::::
2017-12-21 15:53:43.545606+0800 DeadThread[12804:4615777] 線程2 等待ing <NSThread: 0x604000076880>{number = 4, name = (null)} 2017-12-21 15:53:43.545651+0800 DeadThread[12804:4615776] 線程1 等待ing <NSThread: 0x60000026cbc0>{number = 5, name = (null)} 2017-12-21 15:53:50.553973+0800 DeadThread[12804:4615777] 線程2 執行 <NSThread: 0x604000076880>{number = 4, name = (null)} 2017-12-21 15:53:50.553973+0800 DeadThread[12804:4615776] 線程1 執行 <NSThread: 0x60000026cbc0>{number = 5, name = (null)} 2017-12-21 15:53:50.554140+0800 DeadThread[12804:4615777] 線程2 發送信號 <NSThread: 0x604000076880>{number = 4, name = (null)} 2017-12-21 15:53:50.554144+0800 DeadThread[12804:4615776] 線程1 發送信號 <NSThread: 0x60000026cbc0>{number = 5, name = (null)} 2017-12-21 15:53:50.554252+0800 DeadThread[12804:4615776] --------------------------------------------------------
結果分析:::