iOS 中關於@synchronized的一點思考
阿新 • • 發佈:2019-01-11
參考 :
https://www.cnblogs.com/jukaiit/p/5570056.html
https://www.cnblogs.com/CoderAlex/p/5257339.html
http://www.cocoachina.com/ios/20161205/18279.html
http://www.jianshu.com/p/1e59f0970bf5
首先我們知道@synchronized 的作用是建立一個互斥鎖,保證此時沒有其它執行緒對鎖物件進行修改。這個是objective-c的一個鎖定令牌,防止鎖物件在同一時間內被其它執行緒訪問,起到執行緒的保護作用。
指令@synchronized()通過對一段程式碼的使用進行加鎖。其他試圖執行該段程式碼的執行緒都會被阻塞,直到加鎖執行緒退出執行該段被保護的程式碼段
相關術語:
執行緒同步:多天執行緒在同一條線上執行(按順序的執行任務)
互斥鎖就是使用了執行緒同步技術
下面看互斥鎖使用格式
@synchronized (鎖物件) {
加鎖程式碼
}
鎖定一份程式碼只能用一把鎖,多把鎖是無效的。
優點:防止多執行緒操作時資源競爭導致的資料安全問題
缺點:需要消耗大量的CPU資源
互斥鎖的使用前提是:多條執行緒使用同一份資源。
檢視該段的彙編程式碼可知主要使用了兩個函式
_objc_sync_enter
_objc_sync_exit
在Objective-C 中檢視原始碼int objc_sync_enter(id obj) { int result = OBJC_SYNC_SUCCESS; if (obj) { SyncData* data = id2data(obj, ACQUIRE); assert(data); data->mutex.lock(); } else { // @synchronized(nil) does nothing if (DebugNilSync) { _objc_inform("NIL SYNC DEBUG: @synchronized(nil); set a breakpoint on objc_sync_nil to debug"); } objc_sync_nil(); } return result; } // End synchronizing on 'obj'. // Returns OBJC_SYNC_SUCCESS or OBJC_SYNC_NOT_OWNING_THREAD_ERROR int objc_sync_exit(id obj) { int result = OBJC_SYNC_SUCCESS; if (obj) { SyncData* data = id2data(obj, RELEASE); if (!data) { result = OBJC_SYNC_NOT_OWNING_THREAD_ERROR; } else { bool okay = data->mutex.tryUnlock(); if (!okay) { result = OBJC_SYNC_NOT_OWNING_THREAD_ERROR; } } } else { // @synchronized(nil) does nothing } return result; }
檢視SyncData
typedef struct SyncData {
struct SyncData* nextData;
DisguisedPtr<objc_object> object;
int32_t threadCount; // number of THREADS using this block
recursive_mutex_t mutex;
} SyncData;
可見在SyncData 維護了一個遞迴鎖,所以如下程式碼就不會死鎖。
@synchronized (obj) { NSLog(@"13qw"); @synchronized (obj) { NSLog(@"2asda"); } }
但是並不意味著synchronized不會死鎖,類似下面的程式碼還是會導致死鎖的
@synchronized (self) {
[_sharedLock lock];
NSLog(@"code in class A");
[_sharedLock unlock];
}
正確使用@synchronized還需要注意粒度控制
@synchronized (token) {
[arrA addObject:obj];
}
@synchronized (token) {
[arrB addObject:obj];
}
使用同一個token來同步arrA和arrB的訪問,雖然arrA和arrB之間沒有任何聯絡。傳入self的就更不對了。
應該是不同的資料使用不同的鎖,儘量將粒度控制在最細的程度。上述程式碼應該是:
@synchronized (tokenA) {
[arrA addObject:obj];
}
@synchronized (tokenB) {
[arrB addObject:obj];
}
@synchronized(nil)不起任何作用,所以傳參時還是需要做好判斷
注意內部的函式呼叫
@synchronized (tokenA) {
[arrA addObject:obj];
[self doSomething:arrA];
}
在 doSomething:中可能有更多的函式呼叫,這樣就會導致@synchronized更慢,所以建議@synchronized內加鎖的程式碼還是儘量簡單的好,
如果無法避免,還是要做好維護工作