Objective-C中不同方式實現鎖(一)
轉自:http://www.tanhao.me/pieces/616.html/
爲什麼需要使用鎖,當然熟悉多線程的你,自然不會對它覺得陌生。
那你在代碼中是否很好的使用了鎖的機制呢?你又知道幾種實現鎖的方法呢?
今天一起來探討一下Objective-C中幾種不同方式實現的鎖,在這之前我們先構建一個測試用的類,假想它是我們的一個共享資源,method1與method2是互斥的,代碼如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
@implementation TestObj - (void)method1 { NSLog(@"%@",NSStringFromSelector(_cmd)); } - (void)method2 { NSLog(@"%@",NSStringFromSelector(_cmd)); } @end |
1.使用NSLock實現的鎖
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
//主線程中 TestObj *obj = [[TestObj alloc] init]; NSLock *lock = [[NSLock alloc] init]; //線程1 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ [lock lock]; [obj method1]; sleep(10); [lock unlock]; }); //線程2 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ sleep(1);//以保證讓線程2的代碼後執行 [lock lock]; [obj method2]; [lock unlock]; }); |
看到打印的結果了嗎,你會看到線程1鎖住之後,線程2會一直等待走到線程1將鎖置爲unlock後,纔會執行method2方法。
NSLock是Cocoa提供給我們最基本的鎖對象,這也是我們經常所使用的,除lock和unlock方法外,NSLock還提供了tryLock和lockBeforeDate:兩個方法,前一個方法會嘗試加鎖,如果鎖不可用(已經被鎖住),剛並不會阻塞線程,並返回NO。lockBeforeDate:方法會在所指定Date之前嘗試加鎖,如果在指定時間之前都不能加鎖,則返回NO。
2.使用synchronized關鍵字構建的鎖
當然在Objective-C中你還可以用@synchronized指令快速的實現鎖:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
//主線程中 TestObj *obj = [[TestObj alloc] init]; //線程1 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ @synchronized(obj){ [obj method1]; sleep(10); } }); //線程2 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ sleep(1); @synchronized(obj){ [obj method2]; } }); |
@synchronized指令使用的obj爲該鎖的唯一標識,只有當標識相同時,才爲滿足互斥,如果線程2中的@synchronized(obj)改爲@synchronized(other),剛線程2就不會被阻塞,@synchronized指令實現鎖的優點就是我們不需要在代碼中顯式的創建鎖對象,便可以實現鎖的機制,但作爲一種預防措施,@synchronized塊會隱式的添加一個異常處理例程來保護代碼,該處理例程會在異常拋出的時候自動的釋放互斥鎖。所以如果不想讓隱式的異常處理例程帶來額外的開銷,你可以考慮使用鎖對象。
3.使用C語言的pthread_mutex_t實現的鎖
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
//主線程中 TestObj *obj = [[TestObj alloc] init]; __block pthread_mutex_t mutex; pthread_mutex_init(&mutex, NULL); //線程1 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ pthread_mutex_lock(&mutex); [obj method1]; sleep(5); pthread_mutex_unlock(&mutex); }); //線程2 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ sleep(1); pthread_mutex_lock(&mutex); [obj method2]; pthread_mutex_unlock(&mutex); }); |
pthread_mutex_t定義在pthread.h,所以記得#include
4.使用GCD來實現的”鎖”
以上代碼構建多線程我們就已經用到了GCD的dispatch_async方法,其實在GCD中也已經提供了一種信號機制,使用它我們也可以來構建一把”鎖”(從本質意義上講,信號量與鎖是有區別,具體差異參加信號量與互斥鎖之間的區別):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
//主線程中 TestObj *obj = [[TestObj alloc] init]; dispatch_semaphore_t semaphore = dispatch_semaphore_create(1); //線程1 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); [obj method1]; sleep(10); dispatch_semaphore_signal(semaphore); }); //線程2 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ sleep(1); dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); [obj method2]; dispatch_semaphore_signal(semaphore); }); |
至於代碼產生的效果當然和上一例是一模一樣的,關於信號機制,熟悉C編程的你肯定也不會陌生的,關於GCD中更多關於dispatch_semaphore_t的信息,可以跳轉到本博客的這一往篇文章:GCD介紹(三): Dispatch Sources
好了,以上就是我所列舉了幾種方式來實現鎖,當然鎖大多數情況下也是配合多線程一起使用的,關於多線程編程,我這兒就不贅述了。