- (void)viewDidLoad {
[super viewDidLoad];
self.ticketCount = 20;
[self wm_testSaleTicket];
}
- (void)wm_testSaleTicket{
dispatch_async(dispatch_get_global_queue(0, 0), ^{
for (int i = 0; i < 5; i++) {
[self saleTicket];
}
});
dispatch_async(dispatch_get_global_queue(0, 0), ^{
for (int i = 0; i < 5; i++) {
[self saleTicket];
}
});
dispatch_async(dispatch_get_global_queue(0, 0), ^{
for (int i = 0; i < 3; i++) {
[self saleTicket];
}
});
dispatch_async(dispatch_get_global_queue(0, 0), ^{
for (int i = 0; i < 10; i++) {
[self saleTicket];
}
});
}
- (void)saleTicket{
if (self.ticketCount > 0) {
self.ticketCount--;
sleep(0.1);
NSLog(@"当前余票还剩:%ld张",self.ticketCount);
}else{
NSLog(@"当前车票已售罄");
}
}
- (void)saleTicket{
@synchronized (self) {
if (self.ticketCount > 0) {
self.ticketCount--;
sleep(0.1);
NSLog(@"当前余票还剩:%ld张",self.ticketCount);
}else{
NSLog(@"当前车票已售罄");
}
}
}
为什么加了@synchronize就可以了呢?下面我们就去看看它的底层原理吧!!!
打个断点,打开Debug->Debug Workflow->Always Show Disassembly,我们会看到汇编的代码!!!
在汇编的代码中,我们发现两个看着很有感觉的方法!!O(∩_∩)O哈哈~在这段代码中间包裹了一大段代码!我们猜想,这就应该是锁的开始与退出,中间是对逻辑的处理!
我们使用clang去确定一下:我们对main函数做如下处理:
#import
#import "AppDelegate.h"
int main(int argc, char * argv[]) {
NSString * appDelegateClassName;
@autoreleasepool {
appDelegateClassName = NSStringFromClass([AppDelegate class]);
@synchronized (appDelegateClassName) {
}
}
return UIApplicationMain(argc, argv, nil, appDelegateClassName);
}
int main(int argc, char * argv[]) {
NSString * appDelegateClassName;
/* @autoreleasepool */
{ __AtAutoreleasePool __autoreleasepool;
appDelegateClassName = NSStringFromClass(((Class (*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("AppDelegate"), sel_registerName("class")));
//下面的代码块就是@synchronize生成的c++代码,这里就验证了我们的猜想吧!
//objc_sync_enter&_sync_exit
{
id _rethrow = 0;
id _sync_obj = (id)appDelegateClassName;
objc_sync_enter(_sync_obj);//进入
try {
struct _SYNC_EXIT { _SYNC_EXIT(id arg) : sync_exit(arg) {}
~_SYNC_EXIT() {objc_sync_exit(sync_exit);}//析构函数
id sync_exit;
} _sync_exit(_sync_obj);//退出
} catch (id e) {_rethrow = e;}//下面是特殊情况的抛出异常
{ struct _FIN { _FIN(id reth) : rethrow(reth) {}
~_FIN() { if (rethrow) objc_exception_throw(rethrow); }
id rethrow;
} _fin_force_rethow(_rethrow);}
}
}
return UIApplicationMain(argc, argv, __null, appDelegateClassName);
}
//开始同步obj对象。如果需要,分配与'obj'相关的递归互斥量。
//一旦获得锁,返回OBJC_SYNC_SUCCESS。
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");
}
/**
BREAKPOINT_FUNCTION(
void objc_sync_nil(void)
);
*/
objc_sync_nil();
}
return result;
}
//这里顺便把exit的代码也放在这里!!!
//结束同步'obj'。
//成功返回OBJC_SYNC_SUCCESS
//异常返回OBJC_SYNC_NOT_OWNING_THREAD_ERROR
int objc_sync_exit(id obj)
{
int result = OBJC_SYNC_SUCCESS;
if (obj) {
//此处获取SyncData数据对象,传入RELEASE
SyncData* data = id2data(obj, RELEASE);
if (!data) {//如果获取的data不存在,则报出异常
result = OBJC_SYNC_NOT_OWNING_THREAD_ERROR;
} else {
bool okay = data->mutex.tryUnlock();//解锁
if (!okay) {
//解锁失败报出异常
result = OBJC_SYNC_NOT_OWNING_THREAD_ERROR;
}
}
} else {//这里和enter一样,如果obj不存在,什么都不做
// @synchronized(nil) does nothing
}
//加解锁成功返回OBJC_SYNC_SUCCESS
return result;
}
//一般情况下,锁使用还是比较少的
//只在需要的时候给定一个锁。
//又由于锁的使用很少,所以会将锁保存在一个表中!
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;
static SyncData* id2data(id object, enum usage why)
{
/***
取出并行列表中的data和lock,使用并行列表的存储方式,减少不相关对象的资源争用
#define LOCK_FOR_OBJ(obj) sDataLists[obj].lock
#define LIST_FOR_OBJ(obj) sDataLists[obj].data
static StripedMap sDataLists;
StripedMap表,存储的是SyncList结构的数据
struct SyncList {
SyncData *data;
spinlock_t lock;
constexpr SyncList() : data(nil), lock(fork_unsafe_lock) { }
};
StripedMap表中通过indexForPointer下表老获取
static unsigned int indexForPointer(const void *p) {
uintptr_t addr = reinterpret_cast(p);
return ((addr >> 4) ^ (addr >> 9)) % StripeCount; // 哈希函数
}
*/
spinlock_t *lockp = &LOCK_FOR_OBJ(object);
SyncData **listp = &LIST_FOR_OBJ(object);
SyncData* result = NULL;
#if SUPPORT_DIRECT_THREAD_KEYS //有确切的SYNC_DATA_DIRECT_KEY标记时,根据key-value查询速度更快,针对单个线程
// Check per-thread single-entry fast cache for matching object
// 检查每个线程单条目快速缓存是否匹配对象
// tls局部存储空间的表
bool fastCacheOccupied = NO;
//通过key从表中获取存储节点
SyncData *data = (SyncData *)tls_get_direct(SYNC_DATA_DIRECT_KEY);
if (data) {
fastCacheOccupied = YES;
if (data->object == object) {
// 在快速缓存中找匹配
uintptr_t lockCount;
result = data;
//此处获取被锁的次数,说明可以多次进入,允许递归性
lockCount = (uintptr_t)tls_get_direct(SYNC_COUNT_DIRECT_KEY);
//快速获取节点有bug抛出异常
if (result->threadCount <= 0 || lockCount <= 0) {
_objc_fatal("id2data fastcache is buggy");
}
//这里enter传入的why值是ACQUIRE,exit传入的是RELEASE
switch(why) {
case ACQUIRE: {
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) {//如果标记值为0,就从快速缓存中移除
//移除标记的SyncCacheItem.data
tls_set_direct(SYNC_DATA_DIRECT_KEY, NULL);
//对threadCount减量操作
/**
int32_t OSAtomicDecrement32Barrier( volatile int32_t *__theValue ) {
return OSAtomicAdd32Barrier( -1, __theValue);
}
*/
OSAtomicDecrement32Barrier(&result->threadCount);
}
break;
case CHECK:
// do nothing
break;
}
return result;
}
}
#endif
// Check per-thread cache of already-owned locks for matching object
// 检查已拥有锁的每个线程高速缓存中是否有匹配的对象
// 全局的@synchronize锁的表
// 这里没有SYNC_DATA_DIRECT_KEY标记,不能通过key-value值快速查询,针对全部线程查找
SyncCache *cache = fetch_cache(NO);//先去下面看这个函数的实现
//这里cache如果不存在则会走下面的流程了
if (cache) {//找到缓存数据,下面开始匹配object
unsigned int i;
for (i = 0; i < cache->used; i++) {
SyncCacheItem *item = &cache->list[i];
if (item->data->object != object) continue;//不匹配则继续循环查找
//找到匹配object
result = item->data;
if (result->threadCount <= 0 || item->lockCount <= 0) {//抛出异常
_objc_fatal("id2data cache is buggy");
}
//这里逻辑和上面的查找到匹配缓存的逻辑一样
switch(why) {
case ACQUIRE:
item->lockCount++;
break;
case RELEASE:
item->lockCount--;
if (item->lockCount == 0) {
cache->list[i] = cache->list[--cache->used];
OSAtomicDecrement32Barrier(&result->threadCount);
}
break;
case CHECK:
// do nothing
break;
}
return result;
}
}
// 线程缓存没有找到任何东西。
// 自旋锁可以防止多个线程为同一个新对象创建多个锁。
// 如果我们发现有超过20个或更多不同的锁处于活动状态,我们可以将节点保存在某个哈希表中,但我们现在不这样做。
// 在以前的写法里面,多线程性能没有那么优越,所以@synchronize(self)中的self会被锁在不同的SyncData里面
// 这里会进行整个链表结构的搜索,进行偏移到下一个节点nextData
lockp->lock();//此处加锁.锁住到*listp = result;这段代码的操作
{
SyncData* p;
SyncData* firstUnused = NULL;
for (p = *listp; p != NULL; p = p->nextData) {
if ( p->object == object ) {
//此处找到了匹配的object,result被赋值,说明是多个节点的存储,那么也就是多线程的操作
result = p;
//threadCount 增量操作
/**
int32_t OSAtomicIncrement32Barrier( volatile int32_t *__theValue )
{
return OSAtomicAdd32Barrier(1, __theValue);
}*/
OSAtomicIncrement32Barrier(&result->threadCount);
//跳转到done
goto done;
}
if ((firstUnused == NULL) && (p->threadCount == 0))
firstUnused = p;
}
// 没有与当前对象关联的SyncData
if ( (why == RELEASE) || (why == CHECK) )
//调到done,并且why==RELEASE,返回nil;why == CHECK抛出异常
goto done;
//有一个空的SyncData *p可以使用,那么就去设置object为当前的object;
//并且threadCount = 1;
if ( firstUnused != NULL ) {
result = firstUnused;
result->object = (objc_object *)object;
result->threadCount = 1;
goto done;
}
}
//分配一个新的SyncData并添加到列表中。
//并对result的object设置为当前的object
//threadCount = 1
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) {
// 所有的释放、检查和递归获取都由上面的每个线程缓存处理。
// 只有新来的ACQUIRE才能进来
if (why == RELEASE) {//在tls中未找到,但是在其他的SyncData中找到匹配的object,如果是relese进来到这里了,那就返回nil
// 某些线程的对象被另一个线程持有时!
return nil;
}
//抛出异常
if (why != ACQUIRE) _objc_fatal("id2data is buggy");
if (result->object != object) _objc_fatal("id2data is buggy");
#if SUPPORT_DIRECT_THREAD_KEY
//有确切的SUPPORT_DIRECT_THREAD_KEYS
if (!fastCacheOccupied) {
// 保存在快速线程缓存中
//直接保存在tls局部缓存空间
//标记: SYNC_DATA_DIRECT_KEY & SYNC_COUNT_DIRECT_KEY
tls_set_direct(SYNC_DATA_DIRECT_KEY, result);
tls_set_direct(SYNC_COUNT_DIRECT_KEY, (void*)1);
} else
#endif
{
// 保存在线程缓存中
if (!cache) cache = fetch_cache(YES);
cache->list[cache->used].data = result;
cache->list[cache->used].lockCount = 1;
cache->used++;
}
}
return result;
}
- static SyncCache *fetch_cache(bool create)
{
_objc_pthread_data *data;
/**
//_objc_fetch_pthread_data:为这个线程获取objc的pthread数据。
//如果数据还不存在,并且create是NO,返回NULL。
//如果数据还不存在,create是YES,分配内存空间并返回data。
_objc_pthread_data *_objc_fetch_pthread_data(bool create)
{
_objc_pthread_data *data;
data = (_objc_pthread_data *)tls_get(_objc_pthread_key);
if (!data && create) {
data = (_objc_pthread_data *)
calloc(1, sizeof(_objc_pthread_data));
tls_set(_objc_pthread_key, data);
}
return data;
}
*/
data = _objc_fetch_pthread_data(create);
//如果data不存在,则返回NULL
if (!data) return NULL;
//data存在,但是cache不存在
if (!data->syncCache) {
if (!create) {//如果传入的creat是NO,返回NULL,上面方法调用走这里,表示没有获取到缓存
return NULL;
} else {//如果传入的creat是YES,则会给缓存分配内存空间
int count = 4;
data->syncCache = (SyncCache *)
calloc(1, sizeof(SyncCache) + count*sizeof(SyncCacheItem));
data->syncCache->allocated = count;
}
}
// 缓存扩容,确保可以存入新的缓存,避免溢出
if (data->syncCache->allocated == data->syncCache->used) {
data->syncCache->allocated *= 2;
data->syncCache = (SyncCache *)
realloc(data->syncCache, sizeof(SyncCache)
+ data->syncCache->allocated * sizeof(SyncCacheItem));
}
//返回获取到的缓存,这里我们还返回到id2data函数的已存在锁的表查询代码去!!!
return data->syncCache;
}