iOS中锁-synchronize

常见锁的类型:

  1. OSSpinLock
  2. dispatch_semaphore_t
  3. os_unfair_lock
  4. pthread_mutex_t
  5. NSlock
  6. NSCondition
  7. pthread_mutex_t(recursive)
  8. NSRecursiveLock
  9. NSConditionLock
  10. @synchronized

在很多的场景中我们都用到 @synchronized这个锁,我们来分析一下@synchronized的源码

举个?

我们一共有20张票,

截屏2021-08-17 上午11.37.53.png

然后有好几个窗口在同时卖票,

截屏2021-08-17 上午11.38.25.png

截屏2021-08-17 上午11.39.14.png
好了,现在开始卖票

截屏2021-08-17 上午11.39.48.png

但是我们发现,在票为17张的时候下面显示出18张,这样这个票数便不准确了。
这是因为在同一时刻有多个线程同时操作同一块内存区域,导致数据不准确,我们加一个锁,在同一时刻只有一个线程操作这个票数。
截屏2021-08-17 下午12.06.08.png

截屏2021-08-17 下午12.06.26.png

@synchronized

在上面的例子中我们用到了@synchronized锁,它起到一个加锁的效果。有时候我们还会看到下面这种结构。@synchronized是可以实现递归可重入的。那么@synchronized的结构是怎么样子的,是怎么实现加锁的效果的❓。我们来分析源码

@synchronized (p) {

              @synchronized (p1) {

                   
              }
}
复制代码

加个断点,看到汇编走到了

截屏2021-08-17 下午1.37.44.png

截屏2021-08-17 下午1.48.02.png
找到objc的源码,搜索objc_sync_enter

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;

}
复制代码

从else看出,如果obj为空,则does nothing

看下objc_sync_exit

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不为空的情况下,分别进行了data->mutex.lock();bool okay = data->mutex.tryUnlock();操作。

我们看下SyncData* data = id2data(obj, ACQUIRE);主要流程代码


static SyncData* id2data(id object, enum usage why)
{

spinlock_t *lockp = &LOCK_FOR_OBJ(object);

SyncData **listp = &LIST_FOR_OBJ(object);

SyncData* result = NULL;
#if SUPPORT_DIRECT_THREAD_KEYS

bool fastCacheOccupied = NO;
//#define __PTK_FRAMEWORK_OBJC_KEY1 41
SyncData *data = (SyncData *)tls_get_direct(SYNC_DATA_DIRECT_KEY);

//如果有data则走if里面
if(data){...}

SyncCache *cache = fetch_cache(NO);
//Check per-thread cache of already-owned locks for matching object
if (cache) {...}

lockp->lock();
{...}

//创建一个SyncData添加到list
posix_memalign((void **)&result, alignof(SyncData), sizeof(SyncData));

result->object = (objc_object *)object;

result->threadCount = 1;

new (&result->mutex) recursive_mutex_t(fork_unsafe_lock);

result->nextData = *listp;

*listp = result;

done:

    lockp->unlock();
    if (result) {
    //存入cache 等别的操作
    ...
    }
return result;    
复制代码

数据结构

spinlock_t *lockp = &LOCK_FOR_OBJ(object);

SyncData **listp = &LIST_FOR_OBJ(object);
复制代码
#define LOCK_FOR_OBJ(obj) sDataLists[obj].lock

#define LIST_FOR_OBJ(obj) sDataLists[obj].data

static StripedMap<SyncList> sDataLists;

复制代码

我们看下SyncList, StripedMap

struct SyncList {

SyncData *data;

spinlock_t lock;

constexpr SyncList() : data(nil), lock(fork_unsafe_lock) { }

};
template<typename T>

class StripedMap {

#if TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR

enum { StripeCount = 8 };

#else

enum { StripeCount = 64 };

#endif
...
}


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;



复制代码

可以看到sDataLists是一个哈希结构
打印一下,由class StripedMap我们使用的模拟器,所以StripeCount = 64,是下面的结构

(StripedMap<SyncList>::PaddedT [64]) array = {

  [0] = {

    value = {

      data = NULL

      lock = {

        mLock = (_os_unfair_lock_opaque = 0)

      }

    }

  }

  [1] = {

    value = {

      data = NULL

      lock = {

        mLock = (_os_unfair_lock_opaque = 0)

      }

    }

  }

  [2] = {

    value = {

      data = NULL

      lock = {

        mLock = (_os_unfair_lock_opaque = 0)

      }

    }

  }

  [3] = {

    value = {

      data = NULL

      lock = {

        mLock = (_os_unfair_lock_opaque = 0)

      }

    }

  }

  [4] = {

    value = {

      data = NULL

      lock = {

        mLock = (_os_unfair_lock_opaque = 0)

      }

    }

  }
  ...
  [63] = {

    value = {

      data = NULL

      lock = {

        mLock = (_os_unfair_lock_opaque = 0)

      }

    }

  }

}

复制代码

第一个@synchronized流程

第一个@synchronized进到SyncData* id2data中,由于data为空,也没有cache,则先创建

posix_memalign((void **)&result, alignof(SyncData), sizeof(SyncData));

result->object = (objc_object *)object;

result->threadCount = 1;

new (&result->mutex) recursive_mutex_t(fork_unsafe_lock);

result->nextData = *listp;

*listp = result;
复制代码

初始化的threadCount为1
由上面知syncList为如下结构,是一个哈希表,表中每一格代表同一个对象,后面的链表是一个@synchronized锁一次这个对象就一个syncData。
截屏2021-08-17 下午2.32.32.png
当再次执行@synchronized (p1),如果p1和之前的对象不相同,则走

{

SyncData* p;

SyncData* firstUnused = NULL;

for (p = *listp; p != NULL; p = p->nextData) {
//如果没有相同的object,则这个函数内什么也不做
if ( p->object == object ) {//如果对象相同,则增加threadCount

result = p; 

// atomic because may collide with concurrent RELEASE

OSAtomicIncrement32Barrier(&result->threadCount);

goto done;

}

if ( (firstUnused == NULL) && (p->threadCount == 0) )

firstUnused = p;

}


// no SyncData currently associated with object

if ( (why == RELEASE) || (why == CHECK) )

goto done;


// an unused one was found, use it

if ( firstUnused != NULL ) {

result = firstUnused;

result->object = (objc_object *)object;

result->threadCount = 1;

goto done;

}

}

复制代码

最终还是会走

posix_memalign((void **)&result, alignof(SyncData), sizeof(SyncData));

result->object = (objc_object *)object;

result->threadCount = 1;

new (&result->mutex) recursive_mutex_t(fork_unsafe_lock);

result->nextData = *listp;

*listp = result;

复制代码

相同的对象

则走

{

fastCacheOccupied = YES;

if (data->object == object) {

// Found a match in fast cache.

uintptr_t lockCount;


result = data;

lockCount = (uintptr_t)tls_get_direct(SYNC_COUNT_DIRECT_KEY);

if (result->threadCount <= 0 || lockCount <= 0) {

_objc_fatal("id2data fastcache is buggy");

}

switch(why) {

case ACQUIRE: {//走这里,lockCount++;锁的次数加一,可重入,递归

lockCount++;

tls_set_direct(SYNC_COUNT_DIRECT_KEY, (void*)lockCount);

break;

}

case RELEASE:

lockCount--;

tls_set_direct(SYNC_COUNT_DIRECT_KEY, (void*)lockCount);

if (lockCount == 0) {

// remove from fast cache

tls_set_direct(SYNC_DATA_DIRECT_KEY, NULL);

// atomic because may collide with concurrent ACQUIRE

OSAtomicDecrement32Barrier(&result->threadCount);//多线程的体现

}

break;

case CHECK:

// do nothing

break;

}

return result;

}

}
复制代码

TLS 线程局部存储(Thread Local Storage,TLS)是操作系统为线程单独提供的私有空间,通常只有有限的容量
objc_sync_exit的时候则会走如下,lockCount==0的时候,则走OSAtomicDecrement32Barrier(&result->threadCount);线程个数减1

lockCount--;

tls_set_direct(SYNC_COUNT_DIRECT_KEY, (void*)lockCount);

if (lockCount == 0) {

tls_set_direct(SYNC_DATA_DIRECT_KEY, NULL);


OSAtomicDecrement32Barrier(&result->threadCount);

}
复制代码

总结

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享