关于@synchronized
一、结论
1)@synchronized内部使用的是recursive_mutex_lock,也就是递归锁,对于统一线程来说,@synchronized加锁的方法可以重复加锁。
比如代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | - ( 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" ); } } |
输出结果为:
1 2 | 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 可以看成一个函数,加锁的对象是后面传入对象的地址,所以如果加锁对象重新赋值,那么地址会重新该表导致加锁失效。
如果这个对象为nil,那么等于没有加锁。
内部实现源代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 | 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中调用加锁的函数,实现如下
1 2 3 4 5 | 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/d83b3b7d5a5a
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 地球OL攻略 —— 某应届生求职总结
· 提示词工程——AI应用必不可少的技术
· Open-Sora 2.0 重磅开源!
· 周边上新:园子的第一款马克杯温暖上架