-
兄弟们大多都听说过--锁。锁诞生的原因就是为了安全
-
@synchronized
锁应该是我们在开发中用的最多的。今天小小的浅谈一波~
1. @synchronized
简单使用
-
- 首先我们先写一个买票的小demo
#import "ViewController.h"
@interface ViewController ()
@property(nonatomic)NSInteger ticketCounts;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
self.ticketCounts = 30;
[self xg_doPay];
}
//买票
- (void)xg_doPay{
//异步买票
dispatch_async(dispatch_get_global_queue(0, 0), ^{
for (int i = 0;i < 10; i++) {
[self saleTickets];
}
});
dispatch_async(dispatch_get_global_queue(0, 0), ^{
for (int i = 0;i < 15; i++) {
[self saleTickets];
}
});
dispatch_async(dispatch_get_global_queue(0, 0), ^{
for (int i = 0;i < 10; i++) {
[self saleTickets];
}
});
}
//卖票
- (void)saleTickets{
if (self.ticketCounts > 0) {
NSLog(@"当前票数为:%ld",self.ticketCounts);
self.ticketCounts--;
}else{
NSLog(@"没票了~~~");
}
}
由于买票是
异步
的--他没有顺序的,所以经常会造成混乱,也就是不安全
的。
-
- 这个时候,我们经常会做一波操作。(增加一把
@synchronized
锁)
- 这个时候,我们经常会做一波操作。(增加一把
//卖票
- (void)saleTickets{
@synchronized (self) {
if (self.ticketCounts > 0) {
NSLog(@"当前票数为:%ld",self.ticketCounts);
self.ticketCounts--;
}else{
NSLog(@"没票了~~~");
}
}
}
这个时候运行
发现这个时候就没有问题了。
-
- 今天我们就看下。这个
@synchronized
做了什么,他就没有问题了~
- 今天我们就看下。这个
2. @synchronized
底层分析
2.1. 源码定位
对于底层分析,我就会那么几种方法。😆
-
- 首先,我先写个简单的例子,然后看结构
- 首先,我先写个简单的例子,然后看结构
-
- 我们接下来开汇编,打断点
- 我们接下来开汇编,打断点
这样我们就找到了源码位置
2.2. 源码分析
-
- 以下源码的注释也说明了,咱们找到的地方没有错。
objc_sync_enter
- 以下源码的注释也说明了,咱们找到的地方没有错。
// Begin synchronizing on 'obj'.
// Allocates recursive mutex associated with 'obj' if needed.
// Returns OBJC_SYNC_SUCCESS once lock is acquired.
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;
}
objc_sync_exit
// 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;
}
这两个方法,都调用了
id2data
,且返回类型都是SyncData
,那我感觉这个有点东西
-
- 兄弟们看下
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;
-
- 我们第一次看这个结构,然后不多想,看命名给我的感觉就是这样的
- 我们第一次看这个结构,然后不多想,看命名给我的感觉就是这样的
当然,这也只是第一印象,不知道对不对~
给人的感觉就是个单向链表,然后每个节点都存了些东西
-
- 接下来,我们剖析
id2data
函数(这个函数比较长,不过我还是用老方法,折叠大法~)
- 接下来,我们剖析
大概预览下~
-
- 这个
SUPPORT_DIRECT_THREAD_KEYS
的定义
- 这个
-
- 我们那我们研究下这个判断吧
- 我们那我们研究下这个判断吧
-
cache
操作
-
- 在里面继续加锁的操作
- 在里面继续加锁的操作
-
- 然后
done
之后就会解锁
- 然后
done:
lockp->unlock();
if (result) {
// Only new ACQUIRE should get here.
// All RELEASE and CHECK and recursive ACQUIRE are
// handled by the per-thread caches above.
if (why == RELEASE) {
// Probably some thread is incorrectly exiting
// while the object is held by another thread.
return nil;
}
if (why != ACQUIRE) _objc_fatal("id2data is buggy");
if (result->object != object) _objc_fatal("id2data is buggy");
#if SUPPORT_DIRECT_THREAD_KEYS
if (!fastCacheOccupied) {
// Save in fast thread cache
tls_set_direct(SYNC_DATA_DIRECT_KEY, result);
tls_set_direct(SYNC_COUNT_DIRECT_KEY, (void*)1);
} else
#endif
{
// Save in thread cache
if (!cache) cache = fetch_cache(YES);
cache->list[cache->used].data = result;
cache->list[cache->used].lockCount = 1;
cache->used++;
}
}
return result;
}
这个就好多了,有很多注释
-
- 我偷了一幅结构图~
- 我偷了一幅结构图~
以上就是@synchronized
的使用和结构了
继续加班了~~~