溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Objective-C中的@Synchronized關鍵字怎么使用

發(fā)布時間:2023-03-31 16:00:19 來源:億速云 閱讀:67 作者:iii 欄目:開發(fā)技術

這篇文章主要介紹“Objective-C中的@Synchronized關鍵字怎么使用”,在日常操作中,相信很多人在Objective-C中的@Synchronized關鍵字怎么使用問題上存在疑惑,小編查閱了各式資料,整理出簡單好用的操作方法,希望對大家解答”O(jiān)bjective-C中的@Synchronized關鍵字怎么使用”的疑惑有所幫助!接下來,請跟著小編一起來學習吧!

在多線程編程中,線程之間共享資源時容易出現(xiàn)數(shù)據(jù)競爭的問題,導致程序出現(xiàn)不可預期的結果。為了避免這種情況,我們需要采用一些同步機制來保證線程之間的安全協(xié)作。 @synchronized指令是Objective-C中一種常用的同步機制。

@synchronized指令是Objective-C中一種非常簡單方便的創(chuàng)建鎖的方式。相比于其他鎖,它的語法更加簡單,只需要使用任意一個Objective-C對象作為鎖標記即可。

- (void)myMethod:(id)anObj {
    @synchronized(anObj) {
        // Everything between the braces is protected by the @synchronized directive.
    }
}

@synchronized指令中傳遞的對象是用于區(qū)分受保護代碼塊的唯一標識符。如果在兩個不同的線程中執(zhí)行上述方法,分別為anObj參數(shù)傳遞不同的對象,那么每個線程都會獲取自己的鎖并繼續(xù)處理,而不會被另一個線程阻塞。但是,如果在這兩種情況下都傳遞相同的對象,則其中一個線程會首先獲取鎖,另一個線程則會被阻塞,直到第一個線程完成操作。

@Synchronized的底層實現(xiàn)

通過clang查看底層編譯代碼可知, @Synchronized是通過objc_sync_enter和objc_sync_exit函數(shù)來實現(xiàn)鎖的獲取和釋放的,源碼如下:

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;
}
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;
}
  • 如果傳入的obj存在,則走加鎖流程;如果obj為nil,則什么也不做。

  • objc_sync_exit和objc_sync_enter是對應的;objc_sync_exit方法就是解鎖,如果obj= nil則什么也不做;

通過觀察源碼可知,objc_sync_exit和objc_sync_enter里的關鍵是從obj轉換到SyncData,然后通過SyncData中的mutex來對臨界區(qū)上鎖。SyncData結構體的定義如下:

typedef struct alignas(CacheLineSize) SyncData {
    struct SyncData* nextData;
    DisguisedPtr<objc_object> object;
    int32_t threadCount;  // number of THREADS using this block
    recursive_mutex_t mutex;
} SyncData;
  • mutex是遞歸鎖,這也是為什么可以在 @Synchronized里嵌套 @Synchronized的原因了。

從obj轉換到SyncData的具體實現(xiàn)如下:

Objective-C中的@Synchronized關鍵字怎么使用

這段代碼實現(xiàn)了一個鎖的緩存機制,目的是為了提高多線程訪問同一對象時的效率。當多個線程同時訪問同一對象時,每個線程需要獲取一個鎖,這會造成性能瓶頸。為了避免這個問題,緩存機制會將已經獲取的鎖緩存起來,以供下次使用。其大致流程如下:

1、首先檢查是否啟用了快速緩存,如果啟用則在快速緩存中查找是否有與obj對應的SyncData對象。

2、如果在快速緩存中找到了匹配的SyncData對象,則將syncLockCount加1,并返回結果。

3、如果沒有在快速緩存中找到匹配的SyncData對象,則繼續(xù)在線程緩存中查找是否有與obj對應的鎖。

4、如果在線程緩存中找到了匹配的鎖,則將對應鎖的計數(shù)加1,并將其返回結果。

5、如果沒有在線程緩存中找到匹配的鎖,則在全局的哈希表中查找是否有與obj對應的SyncData對象。

6、如果在全局的哈希表中找到了匹配的SyncData對象,則會進行多線程操作,將對應鎖的計數(shù)加1,并返回結果。

7、如果沒有在全局的哈希表中找到匹配的SyncData對象,則創(chuàng)建新對象,并將新對象添加到上述的緩存中,以供下次使用。

badcase分析

#import "ViewController.h"
@interface ViewController ()
@property (nonatomic, strong) NSMutableArray *testArray;
@end
@implementation ViewController
- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.
    self.testArray = @[].mutableCopy;
    for (NSUInteger i = 0; i < 5000; i++) {
        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
            [self testThreadArray];
        });
    }
}
- (void)testThreadArray {
    @synchronized (self.testArray) {
        self.testArray = @[].mutableCopy;
    }
}
@end

運行這段代碼,會出現(xiàn)如下crash:

Objective-C中的@Synchronized關鍵字怎么使用

考慮這個場景,有三個線程A、B、C同時訪問一個非原子屬性self.testArray,初始值為p0。線程A和線程B由于訪問的self.testArray的值一致,產生了競爭,線程A獲取了鎖并將self.testArray的值重新設置為p1,然后釋放了鎖。此時線程C訪問self.testArray,發(fā)現(xiàn)其值為p1,沒有競爭,準備對其進行賦值操作。然而,此時線程B由于之前的鎖已經被釋放,進入代碼塊,也準備對self.testArray進行賦值操作,這會導致兩個線程同時對非原子屬性self.testArray進行賦值操作,從而產生crash。

到此,關于“Objective-C中的@Synchronized關鍵字怎么使用”的學習就結束了,希望能夠解決大家的疑惑。理論與實踐的搭配能更好的幫助大家學習,快去試試吧!若想繼續(xù)學習更多相關知識,請繼續(xù)關注億速云網站,小編會繼續(xù)努力為大家?guī)砀鄬嵱玫奈恼拢?/p>

向AI問一下細節(jié)

免責聲明:本站發(fā)布的內容(圖片、視頻和文字)以原創(chuàng)、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關證據(jù),一經查實,將立刻刪除涉嫌侵權內容。

AI