亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 系統 > iOS > 正文

淺談iOS中的鎖的介紹及使用

2019-10-21 18:43:52
字體:
來源:轉載
供稿:網友

在平時的開發中經常使用到多線程,在使用多線程的過程中,難免會遇到資源競爭的問題,那我們怎么來避免出現這種問題那?

線程安全是什么?

當一個線程訪問數據的時候,其他的線程不能對其進行訪問,直到該線程訪問完畢。簡單來講就是在同一時刻,對同一個數據操作的線程只有一個。只有確保了這樣,才能使數據不會被其他線程影響。而線程不安全,則是在同一時刻可以有多個線程對該數據進行訪問,從而得不到預期的結果。

比如寫文件和讀文件,當一個線程在寫文件的時候,理論上來說,如果這個時候另一個線程來直接讀取的話,那么得到的結果可能是你無法預料的。

怎么來保證線程安全?

通常我們使用鎖的機制來保證線程安全,即確保同一時刻只有同一個線程來對同一個數據源進行訪問。

YY大神 的 不再安全的 OSSpinLock 這邊博客中列出了各種鎖以及性能比較:

iOS,鎖,iOS中的鎖

性能對比

這里性能比較的只是加鎖立馬解鎖的時間消耗,并沒有計算競爭時候的時間消耗。

鎖的介紹及簡單使用

1.@synchronized

@synchronized是 iOS 中最常見的鎖,用法很簡單:

- (void)viewDidLoad {  [super viewDidLoad];  [self synchronized];}- (void)synchronized {  NSObject * cjobj = [NSObject new];    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{    @synchronized(cjobj){      NSLog(@"線程1開始");      sleep(3);      NSLog(@"線程1結束");    }  });    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{    sleep(1);    @synchronized(cjobj){      NSLog(@"線程2");    }  });}

控制臺輸出:

2017-10-18 11:35:13.459194+0800 Thread-Lock[24855:431100] 線程1開始
2017-10-18 11:35:16.460210+0800 Thread-Lock[24855:431100] 線程1結束
2017-10-18 11:35:16.460434+0800 Thread-Lock[24855:431101] 線程2

從上面的控制臺輸出時間可以看出來,在線程 1 內容全部輸出之后,才輸出了線程 2 的內容,“線程1結束”與“線程2”都是在“線程1開始”3 秒后輸出的。

@synchronized(cjobj) 指令使用的 cjobj 為該鎖的唯一標識,只有當標識相同時,才為滿足互斥,如果線程 2 中的 @synchronized(cjobj) 改為 @synchronized(self) ,那么線程 2 就不會被阻塞,@synchronized 指令實現鎖的優點就是我們不需要在代碼中顯式的創建鎖對象,便可以實現鎖的機制,但作為一種預防措施,@synchronized 塊會隱式的添加一個異常處理例程來保護代碼,該處理例程會在異常拋出的時候自動的釋放互斥鎖。所以如果不想讓隱式的異常處理例程帶來額外的開銷,你可以考慮使用鎖對象。

@sychronized(cjobj){} 內部 cjobj 被釋放或被設為 nil 不會影響鎖的功能,但如果 cjobj 一開始就是 nil,那就會丟失了鎖的功能了。

2.NSLock

先看看iOS中NSLock類的.h文件,從代碼中可以看出,該類分成了幾個子類:NSLock、NSConditionLock、NSRecursiveLock、NSCondition,然后有一個 NSLocking 協議:

@protocol NSLocking- (void)lock;- (void)unlock;@end

雖然 NSLock、NSConditionLock、NSRecursiveLock、NSCondition 都遵循的了 NSLocking 協議,但是它們并不相同。

2.1 NSLock

NSLock 實現了最基本的互斥鎖,遵循了 NSLocking 協議,通過 lock 和 unlock 來進行鎖定和解鎖。

源碼內容:

@interface NSLock : NSObject <NSLocking> {@private  void *_priv;}- (BOOL)tryLock;- (BOOL)lockBeforeDate:(NSDate *)limit;@property (nullable, copy) NSString *name API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0));@end

用法:

- (void)viewDidLoad {  [super viewDidLoad];  [self nslock];}- (void)nslock {  NSLock * cjlock = [NSLock new];    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{    [cjlock lock];    NSLog(@"線程1加鎖成功");    sleep(2);    [cjlock unlock];    NSLog(@"線程1解鎖成功");  });    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{    sleep(1);    [cjlock lock];    NSLog(@"線程2加鎖成功");    [cjlock unlock];    NSLog(@"線程2解鎖成功");  });}

控制臺輸出:

2017-10-19 15:03:58.868708+0800 Thread-Lock[39059:846493] 線程1加鎖成功
2017-10-19 15:04:00.872714+0800 Thread-Lock[39059:846493] 線程1解鎖成功
2017-10-19 15:04:00.872722+0800 Thread-Lock[39059:846492] 線程2加鎖成功
2017-10-19 15:04:00.873000+0800 Thread-Lock[39059:846492] 線程2解鎖成功

- (void)nslock {  NSLock * cjlock = [NSLock new];    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{    [cjlock lock];    NSLog(@"線程1加鎖成功");    sleep(2);    [cjlock unlock];    NSLog(@"線程1解鎖成功");  });    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{    if ([cjlock tryLock]) {      NSLog(@"線程3加鎖成功");      [cjlock unlock];      NSLog(@"線程3解鎖成功");    }else {      NSLog(@"線程3加鎖失敗");    }  });}

控制臺輸出:

2017-10-19 15:05:38.627767+0800 Thread-Lock[39118:849171] 線程1加鎖成功
2017-10-19 15:05:38.627767+0800 Thread-Lock[39118:849169] 線程3加鎖失敗
2017-10-19 15:05:40.629969+0800 Thread-Lock[39118:849171] 線程1解鎖成功

- (void)nslock {  NSLock * cjlock = [NSLock new];    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{    [cjlock lock];    NSLog(@"線程1加鎖成功");    sleep(2);    [cjlock unlock];    NSLog(@"線程1解鎖成功");  });  dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{    sleep(3);    if ([cjlock tryLock]) {      NSLog(@"線程4加鎖成功");      [cjlock unlock];      NSLog(@"線程4解鎖成功");    }else {      NSLog(@"線程4加鎖失敗");    }  });}

控制臺輸出:

2017-10-19 15:07:14.872279+0800 Thread-Lock[39166:851060] 線程1加鎖成功
2017-10-19 15:07:16.876108+0800 Thread-Lock[39166:851060] 線程1解鎖成功
2017-10-19 15:07:17.876208+0800 Thread-Lock[39166:851052] 線程4加鎖成功
2017-10-19 15:07:17.876527+0800 Thread-Lock[39166:851052] 線程4解鎖成功

- (void)nslock {  NSLock * cjlock = [NSLock new];    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{    [cjlock lock];    NSLog(@"線程1加鎖成功");    sleep(2);    [cjlock unlock];    NSLog(@"線程1解鎖成功");  });  dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{    if ([cjlock lockBeforeDate:[NSDate dateWithTimeIntervalSinceNow:10]]) {      NSLog(@"線程5加鎖成功");      [cjlock unlock];      NSLog(@"線程5解鎖成功");    }else {      NSLog(@"線程5加鎖失敗");    }  });}

控制臺輸出:

2017-10-19 15:08:39.705131+0800 Thread-Lock[39204:852782] 線程1加鎖成功
2017-10-19 15:08:41.708717+0800 Thread-Lock[39204:852782] 線程1解鎖成功
2017-10-19 15:08:41.708717+0800 Thread-Lock[39204:852784] 線程5加鎖成功
2017-10-19 15:08:41.708935+0800 Thread-Lock[39204:852784] 線程5解鎖成功
注意:lock與unlock操作必須在同一線程,否則結果不確定甚至會引起死鎖

由以上內容總結:

  1. 除 lock 和 unlock 方法外,NSLock 還提供了 tryLock 和 lockBeforeDate:兩個方法。
  2. 由上面的結果可以看到 tryLock 并不會阻塞線程,[cjlock tryLock] 能加鎖返回 YES,不能加鎖返回 NO,然后都會執行后續代碼。
  3. 這里順便提一下 trylock 和 lock 使用場景:當前線程鎖失敗,也可以繼續其它任務,用 trylock 合適;當前線程只有鎖成功后,才會做一些有意義的工作,那就 lock,沒必要輪詢 trylock。以下的鎖都是這樣。
  4. lockBeforeDate: 方法會在所指定 Date 之前嘗試加鎖,會阻塞線程,如果在指定時間之前都不能加鎖,則返回 NO,指定時間之前能加鎖,則返回 YES。
  5. 由于是互斥鎖,當一個線程進行訪問的時候,該線程獲得鎖,其他線程進行訪問的時候,將被操作系統掛起,直到該線程釋放鎖,其他線程才能對其進行訪問,從而卻確保了線程安全。但是如果連續鎖定兩次,則會造成死鎖問題。

2.2 NSRecursiveLock

NSRecursiveLock 是遞歸鎖,顧名思義,可以被一個線程多次獲得,而不會引起死鎖。它記錄了成功獲得鎖的次數,每一次成功的獲得鎖,必須有一個配套的釋放鎖和其對應,這樣才不會引起死鎖。NSRecursiveLock 會記錄上鎖和解鎖的次數,當二者平衡的時候,才會釋放鎖,其它線程才可以上鎖成功。

源碼內容:

@interface NSRecursiveLock : NSObject <NSLocking> {@private  void *_priv;}- (BOOL)tryLock;- (BOOL)lockBeforeDate:(NSDate *)limit;@property (nullable, copy) NSString *name API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0));@end

用法:

- (void)viewDidLoad {  [super viewDidLoad];  [self nsrecursivelock];}- (void)nsrecursivelock{  NSRecursiveLock * cjlock = [[NSRecursiveLock alloc] init];    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{    static void (^RecursiveBlock)(int);    RecursiveBlock = ^(int value) {      [cjlock lock];      NSLog(@"%d加鎖成功",value);      if (value > 0) {        NSLog(@"value:%d", value);        RecursiveBlock(value - 1);      }      [cjlock unlock];      NSLog(@"%d解鎖成功",value);    };    RecursiveBlock(3);  });}

控制臺輸出:

2017-10-19 16:15:40.584213+0800 Thread-Lock[39579:894111] 3加鎖成功
2017-10-19 16:15:40.584387+0800 Thread-Lock[39579:894111] value:3
2017-10-19 16:15:40.584552+0800 Thread-Lock[39579:894111] 2加鎖成功
2017-10-19 16:15:40.584635+0800 Thread-Lock[39579:894111] value:2
2017-10-19 16:15:40.584810+0800 Thread-Lock[39579:894111] 1加鎖成功
2017-10-19 16:15:40.585267+0800 Thread-Lock[39579:894111] value:1
2017-10-19 16:15:40.585714+0800 Thread-Lock[39579:894111] 0加鎖成功
2017-10-19 16:15:40.585906+0800 Thread-Lock[39579:894111] 0解鎖成功
2017-10-19 16:15:40.586138+0800 Thread-Lock[39579:894111] 1解鎖成功
2017-10-19 16:15:40.586217+0800 Thread-Lock[39579:894111] 2解鎖成功
2017-10-19 16:15:40.586314+0800 Thread-Lock[39579:894111] 3解鎖成功

由以上內容總結:

如果用 NSLock 的話,cjlock 先鎖上了,但未執行解鎖的時候,就會進入遞歸的下一層,而再次請求上鎖,阻塞了該線程,線程被阻塞了,自然后面的解鎖代碼不會執行,而形成了死鎖。而 NSRecursiveLock 遞歸鎖就是為了解決這個問題。

2.3 NSConditionLock

NSConditionLock 對象所定義的互斥鎖可以在使得在某個條件下進行鎖定和解鎖,它和 NSLock 類似,都遵循 NSLocking 協議,方法都類似,只是多了一個 condition 屬性,以及每個操作都多了一個關于 condition 屬性的方法,例如 tryLock、tryLockWhenCondition:,所以 NSConditionLock 可以稱為條件鎖。

  1. 只有 condition 參數與初始化時候的 condition 相等,lock 才能正確進行加鎖操作。
  2. unlockWithCondition: 并不是當 condition 符合條件時才解鎖,而是解鎖之后,修改 condition 的值。

源碼內容:

@interface NSConditionLock : NSObject <NSLocking> {@private  void *_priv;}- (instancetype)initWithCondition:(NSInteger)condition NS_DESIGNATED_INITIALIZER;@property (readonly) NSInteger condition;- (void)lockWhenCondition:(NSInteger)condition;- (BOOL)tryLock;- (BOOL)tryLockWhenCondition:(NSInteger)condition;- (void)unlockWithCondition:(NSInteger)condition;- (BOOL)lockBeforeDate:(NSDate *)limit;- (BOOL)lockWhenCondition:(NSInteger)condition beforeDate:(NSDate *)limit;@property (nullable, copy) NSString *name API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0));@end

用法:

- (void)viewDidLoad {  [super viewDidLoad];  [self nsconditionlock];}- (void)nsconditionlock {  NSConditionLock * cjlock = [[NSConditionLock alloc] initWithCondition:0];    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{    [cjlock lock];    NSLog(@"線程1加鎖成功");    sleep(1);    [cjlock unlock];    NSLog(@"線程1解鎖成功");  });    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{    sleep(1);    [cjlock lockWhenCondition:1];    NSLog(@"線程2加鎖成功");    [cjlock unlock];    NSLog(@"線程2解鎖成功");  });    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{    sleep(2);    if ([cjlock tryLockWhenCondition:0]) {      NSLog(@"線程3加鎖成功");      sleep(2);      [cjlock unlockWithCondition:2];      NSLog(@"線程3解鎖成功");    } else {      NSLog(@"線程3嘗試加鎖失敗");    }  });    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{    if ([cjlock lockWhenCondition:2 beforeDate:[NSDate dateWithTimeIntervalSinceNow:10]]) {      NSLog(@"線程4加鎖成功");      [cjlock unlockWithCondition:1];      NSLog(@"線程4解鎖成功");    } else {      NSLog(@"線程4嘗試加鎖失敗");    }  });}

控制臺輸出:

2017-10-19 15:09:44.010992+0800 Thread-Lock[39230:853946] 線程1加鎖成功
2017-10-19 15:09:45.012045+0800 Thread-Lock[39230:853946] 線程1解鎖成功
2017-10-19 15:09:46.012692+0800 Thread-Lock[39230:853947] 線程3加鎖成功
2017-10-19 15:09:48.016536+0800 Thread-Lock[39230:853947] 線程3解鎖成功
2017-10-19 15:09:48.016564+0800 Thread-Lock[39230:853944] 線程4加鎖成功
2017-10-19 15:09:48.017039+0800 Thread-Lock[39230:853944] 線程4解鎖成功
2017-10-19 15:09:48.017040+0800 Thread-Lock[39230:853945] 線程2加鎖成功
2017-10-19 15:09:48.017215+0800 Thread-Lock[39230:853945] 線程2解鎖成功

由以上內容總結:

  1. 在線程 1 解鎖成功之后,線程 2 并沒有加鎖成功,而是繼續等了 1 秒之后線程 3 加鎖成功,這是因為線程 2 的加鎖條件不滿足,初始化時候的 condition 參數為 0,而線程 2
  2. 加鎖條件是 condition 為 1,所以線程 2 加鎖失敗。
  3. lockWhenCondition 與 lock 方法類似,加鎖失敗會阻塞線程,所以線程 2 會被阻塞著。
  4. tryLockWhenCondition: 方法就算條件不滿足,也會返回 NO,不會阻塞當前線程。
  5. lockWhenCondition:beforeDate:方法會在約定的時間內一直等待 condition 變為 2,并阻塞當前線程,直到超時后返回 NO。
  6. 鎖定和解鎖的調用可以隨意組合,也就是說 lock、lockWhenCondition:與unlock、unlockWithCondition: 是可以按照自己的需求隨意組合的。

2.4、NSCondition

NSCondition 是一種特殊類型的鎖,通過它可以實現不同線程的調度。一個線程被某一個條件所阻塞,直到另一個線程滿足該條件從而發送信號給該線程使得該線程可以正確的執行。比如說,你可以開啟一個線程下載圖片,一個線程處理圖片。這樣的話,需要處理圖片的線程由于沒有圖片會阻塞,當下載線程下載完成之后,則滿足了需要處理圖片的線程的需求,這樣可以給定一個信號,讓處理圖片的線程恢復運行。

  1. NSCondition 的對象實際上作為一個鎖和一個線程檢查器,鎖上之后其它線程也能上鎖,而之后可以根據條件決定是否繼續運行線程,即線程是否要進入 waiting 狀態,如果進入 waiting 狀態,當其它線程中的該鎖執行 signal 或者 broadcast 方法時,線程被喚醒,繼續運行之后的方法。
  2. NSCondition 可以手動控制線程的掛起與喚醒,可以利用這個特性設置依賴。

源碼內容:

@interface NSCondition : NSObject <NSLocking> {@private  void *_priv;}- (void)wait; //掛起線程- (BOOL)waitUntilDate:(NSDate *)limit; //什么時候掛起線程- (void)signal; // 喚醒一條掛起線程- (void)broadcast; //喚醒所有掛起線程@property (nullable, copy) NSString *name API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0));@end

用法:

- (void)viewDidLoad {  [super viewDidLoad];  [self nscondition];}- (void)nscondition {  NSCondition * cjcondition = [NSCondition new];    dispatch_async(dispatch_get_global_queue(0, 0), ^{    [cjcondition lock];    NSLog(@"線程1線程加鎖");    [cjcondition wait];    NSLog(@"線程1線程喚醒");    [cjcondition unlock];    NSLog(@"線程1線程解鎖");  });    dispatch_async(dispatch_get_global_queue(0, 0), ^{    [cjcondition lock];    NSLog(@"線程2線程加鎖");    if ([cjcondition waitUntilDate:[NSDate dateWithTimeIntervalSinceNow:10]]) {      NSLog(@"線程2線程喚醒");      [cjcondition unlock];      NSLog(@"線程2線程解鎖");    }  });    dispatch_async(dispatch_get_global_queue(0, 0), ^{    sleep(2);    [cjcondition signal];  });}

控制臺輸出:

2017-10-19 17:15:48.410316+0800 Thread-Lock[40011:943638] 線程1線程加鎖
2017-10-19 17:15:48.410757+0800 Thread-Lock[40011:943640] 線程2線程加鎖
2017-10-19 17:15:50.414288+0800 Thread-Lock[40011:943638] 線程1線程喚醒
2017-10-19 17:15:50.414454+0800 Thread-Lock[40011:943638] 線程1線程解鎖

//如果 [cjcondition signal]; 改成 [cjcondition broadcast];  dispatch_async(dispatch_get_global_queue(0, 0), ^{    sleep(2);    [cjcondition broadcast];  });

控制臺輸出:

2017-10-19 17:18:08.054109+0800 Thread-Lock[40056:946099] 線程1線程加鎖
2017-10-19 17:18:08.054304+0800 Thread-Lock[40056:946096] 線程2線程加鎖
2017-10-19 17:18:10.056071+0800 Thread-Lock[40056:946099] 線程1線程喚醒
2017-10-19 17:18:10.056231+0800 Thread-Lock[40056:946099] 線程1線程解鎖
2017-10-19 17:18:10.056244+0800 Thread-Lock[40056:946096] 線程2線程喚醒
2017-10-19 17:18:10.056445+0800 Thread-Lock[40056:946096] 線程2線程解鎖

由以上內容總結:

  1. 在加上鎖之后,調用條件對象的 wait 或 waitUntilDate: 方法來阻塞線程,直到條件對象發出喚醒信號或者超時之后,再進行之后的操作。
  2. signal 和 broadcast 方法的區別在于,signal 只是一個信號量,只能喚醒一個等待的線程,想喚醒多個就得多次調用,而 broadcast 可以喚醒所有在等待的線程。

3.dispatch_semaphore

dispatch_semaphore 使用信號量機制實現鎖,等待信號和發送信號。

  1. dispatch_semaphore 是 GCD 用來同步的一種方式,與他相關的只有三個函數,一個是創建信號量,一個是等待信號,一個是發送信號。
  2. dispatch_semaphore 的機制就是當有多個線程進行訪問的時候,只要有一個獲得了信號,其他線程的就必須等待該信號釋放。

常用相關API:

dispatch_semaphore_create(long value);dispatch_semaphore_wait(dispatch_semaphore_t _Nonnull dsema, dispatch_time_t timeout);dispatch_semaphore_signal(dispatch_semaphore_t _Nonnull dsema);

用法:

- (void)viewDidLoad {  [super viewDidLoad];  [self dispatch_semaphore];}- (void)dispatch_semaphore {  dispatch_semaphore_t semaphore = dispatch_semaphore_create(1);  dispatch_time_t overTime = dispatch_time(DISPATCH_TIME_NOW, 6 * NSEC_PER_SEC);  dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{    dispatch_semaphore_wait(semaphore, overTime);    NSLog(@"線程1開始");    sleep(5);    NSLog(@"線程1結束");    dispatch_semaphore_signal(semaphore);  });  dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{    sleep(1);    dispatch_semaphore_wait(semaphore, overTime);    NSLog(@"線程2開始");    dispatch_semaphore_signal(semaphore);  });}

控制臺輸出:

2017-10-19 18:30:37.672490+0800 Thread-Lock[40569:993613] 線程1開始
2017-10-19 18:30:42.673845+0800 Thread-Lock[40569:993613] 線程1結束
2017-10-19 18:30:42.674165+0800 Thread-Lock[40569:993612] 線程2開始

//如果 overTime 改成 3 秒

控制臺輸出:
2017-10-19 18:32:32.078186+0800 Thread-Lock[40634:995921] 線程1開始
2017-10-19 18:32:35.082943+0800 Thread-Lock[40634:995920] 線程2開始
2017-10-19 18:32:37.083115+0800 Thread-Lock[40634:995921] 線程1結束

由以上內容總結:

  1. dispatch_semaphore 和 NSCondition 類似,都是一種基于信號的同步方式,但 NSCondition 信號只能發送,不能保存(如果沒有線程在等待,則發送的信號會失效)。而 dispatch_semaphore 能保存發送的信號。dispatch_semaphore 的核心是 dispatch_semaphore_t 類型的信號量。
  2. dispatch_semaphore_create(1) 方法可以創建一個 dispatch_semaphore_t 類型的信號量,設定信號量的初始值為 1。注意,這里的傳入的參數必須大于或等于 0,否則 dispatch_semaphore_create 會返回 NULL。
  3. dispatch_semaphore_wait(semaphore, overTime); 方法會判斷 semaphore 的信號值是否大于 0。大于 0 不會阻塞線程,消耗掉一個信號,執行后續任務。如果信號值為 0,該線程會和 NSCondition 一樣直接進入 waiting 狀態,等待其他線程發送信號喚醒線程去執行后續任務,或者當 overTime 時限到了,也會執行后續任務。
  4. dispatch_semaphore_signal(semaphore); 發送信號,如果沒有等待的線程接受信號,則使 signal 信號值加一(做到對信號的保存)。
  5. 一個 dispatch_semaphore_wait(semaphore, overTime); 方法會去對應一個 dispatch_semaphore_signal(semaphore); 看起來像 NSLock 的 lock 和 unlock,其實可以這樣理解,區別只在于有信號量這個參數,lock unlock 只能同一時間,一個線程訪問被保護的臨界區,而如果 dispatch_semaphore 的信號量初始值為 x ,則可以有 x 個線程同時訪問被保護的臨界區。

4.pthread_mutex 與 pthread_mutex(recursive)

pthread 表示 POSIX thread,定義了一組跨平臺的線程相關的 API,POSIX 互斥鎖是一種超級易用的互斥鎖,使用的時候:

  1. 只需要使用 pthread_mutex_init 初始化一個 pthread_mutex_t,
  2. pthread_mutex_lock 或者 pthread_mutex_trylock 來鎖定 ,
  3. pthread_mutex_unlock 來解鎖,
  4. 當使用完成后,記得調用 pthread_mutex_destroy 來銷毀鎖。

常用相關API:

pthread_mutex_init(pthread_mutex_t *restrict _Nonnull, const pthread_mutexattr_t *restrict _Nullable);pthread_mutex_lock(pthread_mutex_t * _Nonnull);pthread_mutex_trylock(pthread_mutex_t * _Nonnull);pthread_mutex_unlock(pthread_mutex_t * _Nonnull);pthread_mutex_destroy(pthread_mutex_t * _Nonnull);

用法:

//pthread_mutex- (void)viewDidLoad {  [super viewDidLoad];  [self pthread_mutex];}- (void)pthread_mutex {  __block pthread_mutex_t cjlock;  pthread_mutex_init(&cjlock, NULL);    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{    pthread_mutex_lock(&cjlock);    NSLog(@"線程1開始");    sleep(3);    NSLog(@"線程1結束");    pthread_mutex_unlock(&cjlock);      });    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{    sleep(1);    pthread_mutex_lock(&cjlock);    NSLog(@"線程2");    pthread_mutex_unlock(&cjlock);  });}

控制臺輸出:

2017-10-23 14:50:29.842180+0800 Thread-Lock[74478:1647362] 線程1開始
2017-10-23 14:50:32.846786+0800 Thread-Lock[74478:1647362] 線程1結束
2017-10-23 14:50:32.847001+0800 Thread-Lock[74478:1647359] 線程2

//pthread_mutex(recursive)- (void)viewDidLoad {  [super viewDidLoad];  [self pthread_mutex_recursive];}- (void)pthread_mutex_recursive {  __block pthread_mutex_t cjlock;    pthread_mutexattr_t attr;  pthread_mutexattr_init(&attr);  pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);  pthread_mutex_init(&cjlock, &attr);  pthread_mutexattr_destroy(&attr);    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{        static void (^RecursiveBlock)(int);        RecursiveBlock = ^(int value) {      pthread_mutex_lock(&cjlock);      NSLog(@"%d加鎖成功",value);      if (value > 0) {        NSLog(@"value = %d", value);        sleep(1);        RecursiveBlock(value - 1);      }      NSLog(@"%d解鎖成功",value);      pthread_mutex_unlock(&cjlock);    };    RecursiveBlock(3);  });}

//控制臺輸出:
2017-10-23 15:31:51.599693+0800 Thread-Lock[74723:1668089] 3加鎖成功
2017-10-23 15:31:51.599912+0800 Thread-Lock[74723:1668089] value = 3
2017-10-23 15:31:52.602002+0800 Thread-Lock[74723:1668089] 2加鎖成功
2017-10-23 15:31:52.602317+0800 Thread-Lock[74723:1668089] value = 2
2017-10-23 15:31:53.604669+0800 Thread-Lock[74723:1668089] 1加鎖成功
2017-10-23 15:31:53.604957+0800 Thread-Lock[74723:1668089] value = 1
2017-10-23 15:31:54.607778+0800 Thread-Lock[74723:1668089] 0加鎖成功
2017-10-23 15:31:54.608109+0800 Thread-Lock[74723:1668089] 0解鎖成功
2017-10-23 15:31:54.608391+0800 Thread-Lock[74723:1668089] 1解鎖成功
2017-10-23 15:31:54.608622+0800 Thread-Lock[74723:1668089] 2解鎖成功
2017-10-23 15:31:54.608945+0800 Thread-Lock[74723:1668089] 3解鎖成功

由以上內容總結:

  1. 它的用法和 NSLock 的 lock unlock 用法一致,而它也有一個 pthread_mutex_trylock 方法,pthread_mutex_trylock 和 tryLock 的區別在于,tryLock 返回的是 YES 和 NO,pthread_mutex_trylock 加鎖成功返回的是 0,失敗返回的是錯誤提示碼。
  2. pthread_mutex(recursive) 作用和 NSRecursiveLock 遞歸鎖類似。如果使用 pthread_mutex_init(&theLock, NULL); 初始化鎖的話,上面的代碼的第二部分會出現死鎖現象,使用遞歸鎖就可以避免這種現象。

5. OSSpinLock

OSSpinLock 是一種自旋鎖,和互斥鎖類似,都是為了保證線程安全的鎖。但二者的區別是不一樣的,對于互斥鎖,當一個線程獲得這個鎖之后,其他想要獲得此鎖的線程將會被阻塞,直到該鎖被釋放。但自選鎖不一樣,當一個線程獲得鎖之后,其他線程將會一直循環在哪里查看是否該鎖被釋放。所以,此鎖比較適用于鎖的持有者保存時間較短的情況下。

只有加鎖,解鎖,嘗試加鎖三個方法。

常用相關API:

typedef int32_t OSSpinLock;// 加鎖void  OSSpinLockLock( volatile OSSpinLock *__lock );// 嘗試加鎖bool  OSSpinLockTry( volatile OSSpinLock *__lock );// 解鎖void  OSSpinLockUnlock( volatile OSSpinLock *__lock );

用法:

#import <libkern/OSAtomic.h>- (void)viewDidLoad {  [super viewDidLoad];  [self osspinlock];}- (void)osspinlock {  __block OSSpinLock theLock = OS_SPINLOCK_INIT;  dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{    OSSpinLockLock(&theLock);    NSLog(@"線程1開始");    sleep(3);    NSLog(@"線程1結束");    OSSpinLockUnlock(&theLock);      });    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{    OSSpinLockLock(&theLock);    sleep(1);    NSLog(@"線程2");    OSSpinLockUnlock(&theLock);      });}

控制臺輸出:

2017-10-23 16:02:48.865501+0800 Thread-Lock[75025:1684487] 線程1開始
2017-10-23 16:02:51.868736+0800 Thread-Lock[75025:1684487] 線程1結束
2017-10-23 16:02:52.922911+0800 Thread-Lock[75025:1684486] 線程2
YY大神 @ibireme 的文章也有說這個自旋鎖存在優先級反轉問題,具體文章可以戳 不再安全的 OSSpinLock,而 OSSpinLock 在iOS 10.0中被 <os/lock.h> 中的 os_unfair_lock 取代。

6.os_unfair_lock

自旋鎖已經不再安全,然后蘋果又整出來個 os_unfair_lock,這個鎖解決了優先級反轉問題。

常用相關API:

// 初始化os_unfair_lock_t unfairLock = &(OS_UNFAIR_LOCK_INIT);// 加鎖os_unfair_lock_lock(unfairLock);// 嘗試加鎖BOOL b = os_unfair_lock_trylock(unfairLock);// 解鎖os_unfair_lock_unlock(unfairLock);os_unfair_lock 用法和 OSSpinLock 基本一直,就不一一列出了。

總結

應當針對不同的操作使用不同的鎖,而不能一概而論哪種鎖的加鎖解鎖速度快。

  1. 其實每一種鎖基本上都是加鎖、等待、解鎖的步驟,理解了這三個步驟就可以幫你快速的學會各種鎖的用法。
  2. @synchronized 的效率最低,不過它的確用起來最方便,所以如果沒什么性能瓶頸的話,可以選擇使用 @synchronized。
  3. 當性能要求較高時候,可以使用 pthread_mutex 或者 dispath_semaphore,由于 OSSpinLock 不能很好的保證線程安全,而在只有在 iOS10 中才有 os_unfair_lock ,所以,前兩個是比較好的選擇。既可以保證速度,又可以保證線程安全。
  4. 對于 NSLock 及其子類,速度來說 NSLock < NSCondition < NSRecursiveLock < NSConditionLock 。

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持VEVB武林網。


注:相關教程知識閱讀請移步到IOS開發頻道。
發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
成人信息集中地欧美| 色悠久久久久综合先锋影音下载| 国产成人aa精品一区在线播放| 国产免费一区二区三区香蕉精| 欧美激情日韩图片| 日韩免费观看网站| 日韩在线免费视频| 亚洲成人免费网站| 欧美色道久久88综合亚洲精品| 国产精品91在线| 亚洲一区二区免费| 欧美乱妇高清无乱码| 91精品国产成人www| 久久久精品一区二区三区| 色av吧综合网| 日韩在线中文字幕| 日韩精品免费视频| 韩国三级日本三级少妇99| 欧美日韩在线另类| 成人有码视频在线播放| 亚洲成av人乱码色午夜| 亚洲jizzjizz日本少妇| 不卡av电影院| 欧美精品videos性欧美| 在线视频免费一区二区| 亚洲国产另类 国产精品国产免费| 欧美性猛交xxxxx免费看| 在线看国产精品| 国产99久久精品一区二区| 欧美丝袜第一区| 欧美另类69精品久久久久9999| 国产91精品网站| 久久综合国产精品台湾中文娱乐网| 国产精品福利网站| 亚洲春色另类小说| 国产99久久精品一区二区永久免费| 欧美猛交ⅹxxx乱大交视频| 日韩专区在线观看| 亚洲电影免费观看高清| 国产一区二区在线免费视频| 国产精品十八以下禁看| 精品国产一区av| 国产原创欧美精品| 久久精品国产亚洲一区二区| 午夜精品久久久久久久99热浪潮| 欧美精品制服第一页| 国产视频精品在线| 91av视频在线免费观看| 日韩视频第一页| 性亚洲最疯狂xxxx高清| 国产精品精品久久久| 国产精品大陆在线观看| 一级做a爰片久久毛片美女图片| 欧美激情第一页xxx| 久色乳综合思思在线视频| 国产一区玩具在线观看| 97香蕉久久超级碰碰高清版| 91精品国产高清久久久久久| 亚洲精品免费一区二区三区| xvideos成人免费中文版| 欧美日韩国产一区二区| 久国内精品在线| 亚洲激情国产精品| 欧美午夜激情小视频| 亚洲综合社区网| 九九久久久久99精品| 久久精品99久久久久久久久| 日韩一区二区三区xxxx| 日韩免费电影在线观看| 欧美激情中文网| 国产精品精品国产| 91精品视频在线| 日韩高清电影免费观看完整| 欧美日韩亚洲成人| 在线观看久久av| 亚洲风情亚aⅴ在线发布| 欧美性在线观看| 欧美黄网免费在线观看| 欧美丝袜一区二区三区| 欧美成人精品一区二区三区| 亚洲无亚洲人成网站77777| 日本不卡免费高清视频| 日韩在线观看免费网站| 亚洲精品久久7777777| 91精品国产电影| 欧美性猛交xxxx乱大交3| 国产欧洲精品视频| 精品久久久免费| 亚洲xxxx做受欧美| 久久精品99无色码中文字幕| 久久精品成人动漫| 麻豆一区二区在线观看| 亚洲人成网7777777国产| 黑人巨大精品欧美一区二区一视频| 久久理论片午夜琪琪电影网| 色噜噜狠狠狠综合曰曰曰| 欧美不卡视频一区发布| 亚洲最大的网站| 国产又爽又黄的激情精品视频| 国产男女猛烈无遮挡91| 欧美日韩国产综合视频在线观看中文| 欧美激情一区二区久久久| 国外日韩电影在线观看| 亚洲欧美日韩第一区| 欧美美最猛性xxxxxx| 久久久999精品| 91精品国产91久久久久| 成人精品视频久久久久| 国产精品综合不卡av| 久久国产精品99国产精| 日本久久久久亚洲中字幕| 欧美性受xxxx黑人猛交| 国产99久久精品一区二区 夜夜躁日日躁| 亚洲91精品在线观看| 97国产精品视频人人做人人爱| 国产日本欧美一区二区三区在线| 亚洲精品视频中文字幕| 亚洲天堂2020| 国产成人高潮免费观看精品| 亚洲国产精品悠悠久久琪琪| 国产欧美最新羞羞视频在线观看| 麻豆国产va免费精品高清在线| 国产精品成人v| 欧美精品成人91久久久久久久| 亚洲天堂av在线免费观看| 色偷偷av亚洲男人的天堂| 中文字幕国产亚洲| 91免费精品视频| 亚洲一区中文字幕| 国产精品露脸av在线| 在线亚洲午夜片av大片| 精品av在线播放| 另类少妇人与禽zozz0性伦| 亚洲成人精品久久| 成人午夜小视频| 精品电影在线观看| 欧美大片大片在线播放| 91久久国产精品| 色777狠狠综合秋免鲁丝| 欧美亚洲另类视频| 97香蕉超级碰碰久久免费软件| 欧洲中文字幕国产精品| 在线视频亚洲欧美| 精品视频中文字幕| 欧美性猛交xxxx免费看漫画| 92国产精品久久久久首页| 久久免费国产精品1| 欧美与欧洲交xxxx免费观看| 亚洲欧美一区二区三区久久| 97免费视频在线| 亚洲国产欧美在线成人app| 日韩成人xxxx| 青青草精品毛片| 日韩欧美在线视频免费观看| 国产精品激情av在线播放| 97在线观看免费高清| 欧日韩不卡在线视频| 国产精品69久久久久| 亚洲精品一区久久久久久| 91久久夜色精品国产网站| 亚洲精品456在线播放狼人| 91免费综合在线| 91视频88av| 亚洲成色777777在线观看影院|