iOS 锁 部分三
2020-08-02 本文已影响0人
飞不越疯人院
主要讲解锁@synchronized/dispatch_semaphore_t的基本用法
文中使用的 objc4源码是objc4-781和 objc4-723版本;
文中使用的 libplatform 源码版本为libplatform-177.270.1版本
常见锁的分类:
- 自旋锁
OSSpinLock
- 互斥锁
os_unfair_lock
- 互斥/递归/条件锁
pthread_mutex_t
- 互斥锁
NSLock
- 递归锁
NSRecursiveLock
- 条件锁
NSCondition
- 条件锁
NSConditionLock
- 递归锁
@synchronized
- 信号量
semaphore
- 读写锁
pthread_rwlock_t
- 异步栅栏
dispatch_barrier_async
iOS 锁 部分一
iOS 锁 部分二
iOS 锁 部分三
iOS 锁 部分四
1. 递归锁@synchronized
特点
- 网上很多教程都说
@synchronized
是一个基于pthread_mutex_t
封装的递归锁, 但是这是在objc4-750
版本之前(iOS12
之前)是这样, 验证步骤详见补充部分; 从objc4-750
版本开始, 以后的(iOS12之后
)这个锁的实现已经变了, 底层的封装变为了os_unfair_lock
;下面来验证它
我们加一个断点后看下他的汇编调用过程, 最终可以确定@synchronized
调用了两个方法objc_sync_enter
和objc_sync_exit
;
下面去objc
源码中查看下这俩方法的实现;
// 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) {
///根据传入的对象, 来获取一个锁. 所以@sycnhronized 时传入的对象很重要;
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;
}
===>
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;
===>
///recursive_mutex_t的定义为
using recursive_mutex_t = recursive_mutex_tt<LOCKDEBUG>;
===>
///recursive_mutex_tt的底层结构为
class recursive_mutex_tt : nocopy_t {
os_unfair_recursive_lock mLock;
public:
constexpr recursive_mutex_tt() : mLock(OS_UNFAIR_RECURSIVE_LOCK_INIT) {
lockdebug_remember_recursive_mutex(this);
}
constexpr recursive_mutex_tt(const fork_unsafe_lock_t unsafe)
: mLock(OS_UNFAIR_RECURSIVE_LOCK_INIT)
{ }
void lock()
...
}
到了os_unfair_recursive_lock
这一层 objc4
源码就不再有更进一步的结构, 但是我们从libplatform
中可以进行进一步的探究
下载后找到
lock_private.h
文件
///iOS12之后才是这种结构
#define OS_UNFAIR_RECURSIVE_LOCK_AVAILABILITY \
__OSX_AVAILABLE(10.14) __IOS_AVAILABLE(12.0) \
__TVOS_AVAILABLE(12.0) __WATCHOS_AVAILABLE(5.0)
===>
/*!
* @typedef os_unfair_recursive_lock
*
* @abstract
* Low-level lock that allows waiters to block efficiently on contention.
*
* @discussion
* See os_unfair_lock.
*/
OS_UNFAIR_RECURSIVE_LOCK_AVAILABILITY
typedef struct os_unfair_recursive_lock_s {
///底层为互斥锁os_unfair_lock
os_unfair_lock ourl_lock;
///因为@synchronized 为递归锁, 所以需要记录加锁次数
uint32_t ourl_count;
} os_unfair_recursive_lock, *os_unfair_recursive_lock_t;
至此可以验证@synchronized
为一个封装了pthread_mutex_t
或者os_unfair_lock
的递归锁;
- 可能会用到的方法
传入一个对象obj
进行加锁, 如果传入空, 则不执行操作;
@synchronized(obj) { ... }
- 测试代码
#import "ViewController9.h"
@interface ViewController9 ()
@end
@implementation ViewController9
- (void)viewDidLoad {
[super viewDidLoad];
[self recuresiveAction];
}
- (void)recuresiveAction {
static int count = 10;
@synchronized ([self class]) {
NSLog(@"count: %d", count);
if (count > 0) {
count --;
[self recuresiveAction];
}
}
}
@end
2. 信号量dispatch_semaphore
特点
- 本来使用于控制线程的最大并发数量的, 我们将并发数量设置为1也可以认为是加锁的功能;
- 可能会用到的方法
2.1 初始化dispatch_semaphore_create()
传入的值为最大并发数量, 设置为1则达到加锁效果;
2.2 判断信号量的值dispatch_semaphore_wait()
如果大于0, 则可以继续往下执行(同时信号量的值减去一),如果信号量的值为0, 则线程进入休眠状态等待(此方法的第二个参数就是设置要等多久, 一般都是使用永久DISPATCH_TIME_FOREVER
) ;
2.3 释放信号量dispatch_semaphore_signal()
同时使信号量的值加上一; - 测试代码
#import "ViewController10.h"
@interface ViewController10 ()
@property (nonatomic, strong) dispatch_semaphore_t semaphore;
@end
@implementation ViewController10
- (void)viewDidLoad {
[super viewDidLoad];
self.semaphore = dispatch_semaphore_create(1);
[self handleMoney];
}
- (void)handleMoney {
self.money = 100;
__weak typeof(self) weakSelf = self;
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
dispatch_async(queue, ^{
for (int i = 0; i < 5; i ++) {
[weakSelf saveMoney];
}
});
dispatch_async(queue, ^{
for (int i = 0; i < 5; i ++) {
[weakSelf drawMoney];
}
});
}
- (void)saveMoney{
dispatch_semaphore_wait(self.semaphore, DISPATCH_TIME_FOREVER);
[super saveMoney];
dispatch_semaphore_signal(self.semaphore);
}
- (void)drawMoney {
dispatch_semaphore_wait(self.semaphore, DISPATCH_TIME_FOREVER);
[super drawMoney ];
dispatch_semaphore_signal(self.semaphore);
}
@end
补充
1. 在objc4-750之前的@synchronized底层封装的是 pthread_mutex_t
验证过程, 使用objc4-723
版本进行查看
// 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) {
///根据传入的对象, 来获取一个锁. 所以@sycnhronized 时传入的对象很重要;
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;
}
===>
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;
===>
///recursive_mutex_t的定义为
using recursive_mutex_t = recursive_mutex_tt<LOCKDEBUG>;
===>
///在 objc4-723版本中recursive_mutex_tt的底层结构为
class recursive_mutex_tt : nocopy_t {
///底层封装的是互斥锁pthread_mutex_t
pthread_mutex_t mLock;
public:
recursive_mutex_tt() : mLock(PTHREAD_RECURSIVE_MUTEX_INITIALIZER) {
lockdebug_remember_recursive_mutex(this);
}
recursive_mutex_tt(const fork_unsafe_lock_t unsafe)
: mLock(PTHREAD_RECURSIVE_MUTEX_INITIALIZER)
{ }
...
}