iOS 中关于@synchronized的一点思考

来源:互联网 发布:股票交易数据分析 编辑:程序博客网 时间:2024/06/05 17:59

参考 :

https://www.cnblogs.com/jukaiit/p/5570056.html

https://www.cnblogs.com/CoderAlex/p/5257339.html

http://www.cocoachina.com/ios/20161205/18279.html

http://www.jianshu.com/p/1e59f0970bf5

首先我们知道@synchronized 的作用是创建一个互斥锁,保证此时没有其它线程对锁对象进行修改。这个是objective-c的一个锁定令牌,防止锁对象在同一时间内被其它线程访问,起到线程的保护作用。

指令@synchronized()通过对一段代码的使用进行加锁。其他试图执行该段代码的线程都会被阻塞,直到加锁线程退出执行该段被保护的代码段

相关术语:

线程同步:多天线程在同一条线上执行(按顺序的执行任务)

互斥锁就是使用了线程同步技术

下面看互斥锁使用格式

 @synchronized (锁对象) {         加锁代码        }

锁定一份代码只能用一把锁,多把锁是无效的。

优点:防止多线程操作时资源竞争导致的数据安全问题

缺点:需要消耗大量的CPU资源


互斥锁的使用前提是:多条线程使用同一份资源。


查看该段的汇编代码可知主要使用了两个函数

 _objc_sync_enter  _objc_sync_exit
在Objective-C 中查看源码
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;}// End synchronizing on 'obj'. // Returns OBJC_SYNC_SUCCESS or OBJC_SYNC_NOT_OWNING_THREAD_ERRORint 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;}


查看SyncData

typedef struct SyncData {    struct SyncData* nextData;    DisguisedPtr<objc_object> object;    int32_t threadCount;  // number of THREADS using this block    recursive_mutex_t mutex;} SyncData;

可见在SyncData 维护了一个递归锁,所以如下代码就不会死锁。

@synchronized (obj) {    NSLog(@"13qw");    @synchronized (obj) {        NSLog(@"2asda");    }}
 

但是并不意味着synchronized不会死锁,类似下面的代码还是会导致死锁的

@synchronized (self) {    [_sharedLock lock];    NSLog(@"code in class A");    [_sharedLock unlock];}

正确使用@synchronized还需要注意粒度控制
 
@synchronized (token) {    [arrA addObject:obj];}@synchronized (token) {    [arrB addObject:obj];}

使用同一个token来同步arrA和arrB的访问,虽然arrA和arrB之间没有任何联系。传入self的就更不对了。

应该是不同的数据使用不同的锁,尽量将粒度控制在最细的程度。上述代码应该是:

@synchronized (tokenA) {    [arrA addObject:obj];}@synchronized (tokenB) {    [arrB addObject:obj];}

@synchronized(nil)不起任何作用,所以传参时还是需要做好判断


注意内部的函数调用

@synchronized (tokenA) {
    [arrA addObject:obj];
    [self doSomething:arrA];
}

在 doSomething:中可能有更多的函数调用,这样就会导致@synchronized更慢,所以建议@synchronized内加锁的代码还是尽量简单的好,
如果无法避免,还是要做好维护工作






原创粉丝点击