關於@synchronized

1、結論swift

  1)@synchronized內部使用的是recursive_mutex_lock,也就是遞歸鎖,對於統一線程來講,@synchronized加鎖的方法能夠重複加鎖。安全

  好比代碼:多線程

  

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    [self testA];
}

- (void)testA
{
    @synchronized(self)
    {
        NSLog(@"AAAAAA");
        [self testB];
    }
}

- (void)testB
{
    @synchronized(self)
    {
        NSLog(@"BBBBBB");
    }
}

   輸出結果爲:函數

  

2018-08-21 15:25:51.058333+0800 TestSynchronized2[17367:7864821] AAAAAA
2018-08-21 15:25:51.058372+0800 TestSynchronized2[17367:7864821] BBBBBB

  

  2)@synchronized 能夠當作一個函數,加鎖的對象是後面傳入對象的地址,因此若是加鎖對象從新賦值,那麼地址會從新該表致使加鎖失效。ui

     若是這個對象爲nil,那麼等於沒有加鎖。this

    內部實現源代碼以下:線程

    

static void _I_Demo_synchronizedTest(Demo * self, SEL _cmd) { 
    NSMutableArray *arr; 
    {
      id _sync_obj = (id)arr;
      objc_sync_enter(_sync_obj); // 同步鎖進入,參數是arr 
        try { 
            struct _SYNC_EXIT {
                _SYNC_EXIT(id arg) : sync_exit(arg) {}  
                ~_SYNC_EXIT() {objc_sync_exit(sync_exit); // 同步鎖退出,參數是arr 
  } 
            id sync_exit;
        }   _sync_exit(_sync_obj);// 調用結構體的構造函數,參數是arr 
          } catch (id e) { 
      } 
   }
}

int objc_sync_enter(id obj)
    { 
              int result = OBJC_SYNC_SUCCESS;
              if (obj) {
            // 根據obj獲取對應的SyncData節點,id2data函數在下面有解析
            SyncData* data = id2data(obj, ACQUIRE);// 上鎖 
            result = recursive_mutex_lock(&data->mutex); } 
        else 
          { // @synchronized(nil) does nothing 
    }
       return result;
    }

typedef struct SyncData {
             struct SyncData* nextData; // 指向下一個SyncData節點的指針
             DisguisedPtr<objc_object> object; // @synchronized的參數obj
             int32_t threadCount; // number of THREADS using this block   
             recursive_mutex_t mutex; // 遞歸鎖
          } SyncData;

        struct SyncList {
               SyncData *data; // 單鏈表頭指針 
               spinlock_t lock; // 保證多線程安全訪問該鏈表 
               SyncList() : data(nil) { }
        };

static StripedMap<SyncList> sDataLists; // 哈希表,key:obj,value:單鏈表

      // 根據obj獲取對應的SyncData節點static SyncData* id2data(id object, enum usage why)
      { 
          spinlock_t *lockp = &LOCK_FOR_OBJ(object); // SyncList鎖
         SyncData **listp = &LIST_FOR_OBJ(object); // obj對應的SyncData節點所在的
        SyncList SyncData* result = NULL;// 這裏省略一大坨cache代碼 

        lockp->lock(); 
        {
            SyncData* p; 
            SyncData* firstUnused = NULL;
       // 遍歷單鏈表 
            for (p = *listp; p != NULL; p = p->nextData) { 
                  if ( p->object == object ) {
              // 找到obj對應的SyncData節點 
                  result = p; 
                // SyncData節點對應的線程數加1  
                 OSAtomicIncrement32Barrier(&result->threadCount); 
                  goto done; 
        }
    // SyncData節點對應的遞歸鎖沒有線程在用了,回收重用,能夠節省節點建立的時間和空間 
      if ( (firstUnused == NULL) && (p->threadCount == 0) ) 
                    firstUnused = p; 
            }
     // 鏈表中尚未obj對應的SyncData節點,可是有可重用的SyncData節點
    // an unused one was found, use it
             if ( firstUnused != NULL ) {
                  result = firstUnused;
                  result->object = (objc_object *)object;
                  result->threadCount = 1;
                  goto done;
            }
        }
// 鏈表中尚未obj對應的SyncData節點,並且沒有可重用的SyncData節點
       result = (SyncData*)calloc(sizeof(SyncData), 1);
       result->object = (objc_object *)object;
       result->threadCount = 1;
       new (&result->mutex) recursive_mutex_t();
// 新建的SyncData節點往鏈表頭部加 
       result->nextData = *listp;
       *listp = result;
 done:
       lockp->unlock();
       return result;}

  

  3)swift中沒有對應的方法,可是依然能夠使用OC中調用加鎖的函數,實現以下指針

  

func synchronized<T>(_ lock: AnyObject, _ body: () throws -> T) rethrows -> T {
        objc_sync_enter(lock)
        defer { objc_sync_exit(lock) }
        return try body()
    }

  

 

參考資料:對象

  做者:悲觀患者
  連接:https://www.jianshu.com/p/d83b3b7d5a5ablog

相關文章
相關標籤/搜索