spinlock_t
OSSpinLock API 简单使用
OSSpinLock API
很简单,首先看下使用示例。
#import "ViewController.h"
#import <libkern/OSAtomic.h> // 引入 OSSpinLock
@interface ViewController ()
@property (nonatomic, assign) NSInteger sum;
@property (nonatomic, assign) OSSpinLock lock;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
self.lock = OS_SPINLOCK_INIT; // 初始化锁
dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); // 取得一个全局并发队列
self.sum = 0; // sum 从 0 开始
dispatch_async(globalQueue, ^{ // 异步任务 1
OSSpinLockLock(&_lock); // 获得锁
for (unsigned int i = 0; i < 10000; ++i) {
self.sum++;
}
NSLog(@"⏰⏰⏰ %ld", self.sum);
OSSpinLockUnlock(&_lock); // 解锁
});
dispatch_async(globalQueue, ^{ // 异步任务 2
OSSpinLockLock(&_lock); // 获得锁
for (unsigned int i = 0; i < 10000; ++i) {
self.sum++;
}
NSLog(@"⚽️⚽️⚽️ %ld", self.sum);
OSSpinLockUnlock(&_lock); // 解锁
});
}
@end
// 打印 ?️:
⏰⏰⏰ 10000
⚽️⚽️⚽️ 20000
// 把 lock 注释后,运行多次可总结出如下三种不同情况,
// 其中只有一个任务达到了 10000 以上另一个是 10000 以下,另一种是两者都达到了 10000 以上
// 情况 1:
⏰⏰⏰ 9064
⚽️⚽️⚽️ 13708
// 情况 2:
⏰⏰⏰ 11326
⚽️⚽️⚽️ 9933
// 情况 3:
⏰⏰⏰ 10906
⚽️⚽️⚽️ 11903
...
在不加锁情况下打印的数字有一些有趣的点,这里分析一下:(假设在全局并发队列下的两个 dispatch_async
任务都开启了新线程,并把两条线分别命名为 “⏰线程” 和 “⚽️线程”)
1.可以确定的是 ⏰线程 和 ⚽️线程 不会有任何一个可以打印 20000
。
2.⏰线程 和 ⚽️线程 两者的打印都到了10000
以上。
3.⏰线程 或 ⚽️线程 其中一个打印在 10000
以上一个在 10000
以下。
OSSpinLockDeprecated.h 文件内容
下面直接查看 OSSpinLockDeprecated.h
中的代码内容
上面示例代码中每一行与 OSSpinLock
相关的代码都会有这样一行警告 ⚠️⚠️'OSSpinLock' is deprecated: first deprecated in iOS 10.0 - Use os_unfair_lock() from <os/lock.h> instead
。正是由下面的 OSSPINLOCK_DEPRECATED
所提示,在 4 大系统中都提示我们都不要再用 OSSpinLock
了。
#ifndef OSSPINLOCK_DEPRECATED
#define OSSPINLOCK_DEPRECATED 1
#define OSSPINLOCK_DEPRECATED_MSG(_r) "Use " #_r "() from <os/lock.h> instead"
#define OSSPINLOCK_DEPRECATED_REPLACE_WITH(_r) \
__OS_AVAILABILITY_MSG(macosx, deprecated=10.12, OSSPINLOCK_DEPRECATED_MSG(_r)) \
__OS_AVAILABILITY_MSG(ios, deprecated=10.0, OSSPINLOCK_DEPRECATED_MSG(_r)) \
__OS_AVAILABILITY_MSG(tvos, deprecated=10.0, OSSPINLOCK_DEPRECATED_MSG(_r)) \
__OS_AVAILABILITY_MSG(watchos, deprecated=3.0, OSSPINLOCK_DEPRECATED_MSG(_r))
#else
#undef OSSPINLOCK_DEPRECATED
#define OSSPINLOCK_DEPRECATED 0
#define OSSPINLOCK_DEPRECATED_REPLACE_WITH(_r)
#endif
下面是不同情况下的 OSSpinLock API
实现:
1.#if !(defined(OSSPINLOCK_USE_INLINED) && OSSPINLOCK_USE_INLINED)
为真不使用内联时的原始 API:
-
#define OS_SPINLOCK_INIT 0
初始化。
/*! @abstract The default value for an <code>OSSpinLock</code>. OSSpinLock 的默认值是 0(unlocked 状态)
@discussion
The convention is that unlocked is zero, locked is nonzero. 惯例是: unlocked 时是零,locked 时时非零
*/
#define OS_SPINLOCK_INIT 0
-
OSSpinLock
数据类型。
/*! @abstract Data type for a spinlock. 自旋锁的数据类型是 int32_t
@discussion
You should always initialize a spinlock to {@link OS_SPINLOCK_INIT} before using it.
在使用一个自旋锁之前,我们应该总是先把它初始化为 OS_SPINLOCK_INIT。(其实是对它赋值为数字 0)
*/
typedef int32_t OSSpinLock OSSPINLOCK_DEPRECATED_REPLACE_WITH(os_unfair_lock);
-
OSSpinLockTry
尝试加锁,bool
类型的返回值表示是否加锁成功,即使加锁失败也不会阻塞线程。
/*! @abstract Locks a spinlock if it would not block. 如果一个 spinlock 未锁定,则锁定它。
@result
Returns <code>false</code> if the lock was already held by another thread,
<code>true</code> if it took the lock successfully.
如果锁已经被另一个线程所持有则返回 false,否则返回 true 表示加锁成功。
*/
OSSPINLOCK_DEPRECATED_REPLACE_WITH(os_unfair_lock_trylock)
__OSX_AVAILABLE_STARTING(__MAC_10_4, __IPHONE_2_0)
bool OSSpinLockTry( volatile OSSpinLock *__lock );
-
OSSpinLockLock
加锁。
/*! @abstract Locks a spinlock. 锁定一个 spinlock
@discussion
Although the lock operation spins, it employs various strategies to back
off if the lock is held.
尽管锁定操作旋转,(当加锁失败时会一直处于等待状态,一直到获取到锁为止,获取到锁之前会一直处于阻塞状态)
它采用各种策略来支持如果加锁成功,则关闭旋转。
*/
OSSPINLOCK_DEPRECATED_REPLACE_WITH(os_unfair_lock_lock)
__OSX_AVAILABLE_STARTING(__MAC_10_4, __IPHONE_2_0)
void OSSpinLockLock( volatile OSSpinLock *__lock );
-
OSSpinLockUnlock
解锁。
/*! @abstract Unlocks a spinlock */
OSSPINLOCK_DEPRECATED_REPLACE_WITH(os_unfair_lock_unlock)
__OSX_AVAILABLE_STARTING(__MAC_10_4, __IPHONE_2_0)
void OSSpinLockUnlock( volatile OSSpinLock *__lock );
2.OSSPINLOCK_USE_INLINED
为真使用内联,内联实现是使用 os_unfair_lock_t
代替 OSSpinLock
。
在函数前加 OSSPINLOCK_INLINE
告诉编译器尽最大努力保证被修饰的函数内联实现。
#if __has_attribute(always_inline) // 尽最大努力保证函数内联实现
#define OSSPINLOCK_INLINE static __inline
#else
#define OSSPINLOCK_INLINE static __inline __attribute__((__always_inline__))
#endif
#define OS_SPINLOCK_INIT 0 // 初始化为 0
typedef int32_t OSSpinLock; // 类型依然是 int32_t
#if __has_extension(c_static_assert)
// 如果 OSSpinLock 和 os_unfair_lock 内存长度不同,即类型不兼容,不能保证双方能正确的转换,直接断言。
_Static_assert(sizeof(OSSpinLock) == sizeof(os_unfair_lock), "Incompatible os_unfair_lock type");
#endif
-
os_unfair_lock
加锁。
OSSPINLOCK_INLINE
void
OSSpinLockLock(volatile OSSpinLock *__lock)
{
// 转换为 os_unfair_lock_t。
os_unfair_lock_t lock = (os_unfair_lock_t)__lock;
return os_unfair_lock_lock(lock);
}
-
os_unfair_lock
尝试加锁。
OSSPINLOCK_INLINE
bool
OSSpinLockTry(volatile OSSpinLock *__lock)
{
// 转换为 os_unfair_lock_t。
os_unfair_lock_t lock = (os_unfair_lock_t)__lock;
return os_unfair_lock_trylock(lock);
}
-
os_unfair_lock
解锁。
OSSPINLOCK_INLINE
void
OSSpinLockUnlock(volatile OSSpinLock *__lock)
{
// 转换为 os_unfair_lock_t。
os_unfair_lock_t lock = (os_unfair_lock_t)__lock;
return os_unfair_lock_unlock(lock);
}
#undef OSSPINLOCK_INLINE
解除上面的宏定义。
3.最后一种情况。
#define OS_SPINLOCK_INIT 0 // 初始化
typedef int32_t OSSpinLock OSSPINLOCK_DEPRECATED_REPLACE_WITH(os_unfair_lock); // 类型 int32_t
typedef volatile OSSpinLock *_os_nospin_lock_t
OSSPINLOCK_DEPRECATED_REPLACE_WITH(os_unfair_lock_t); // 命名 _os_nospin_lock_t
OSSPINLOCK_DEPRECATED_REPLACE_WITH(os_unfair_lock_lock)
OS_NOSPIN_LOCK_AVAILABILITY
void _os_nospin_lock_lock(_os_nospin_lock_t lock); // 加锁
#undef OSSpinLockLock // 解除上面的原始 API 的加锁的宏定义
#define OSSpinLockLock(lock) _os_nospin_lock_lock(lock)
OSSPINLOCK_DEPRECATED_REPLACE_WITH(os_unfair_lock_trylock)
OS_NOSPIN_LOCK_AVAILABILITY
bool _os_nospin_lock_trylock(_os_nospin_lock_t lock); // 尝试加锁
#undef OSSpinLockTry // 解除上面的原始 API 的判断能否加锁的宏定义
#define OSSpinLockTry(lock) _os_nospin_lock_trylock(lock)
OSSPINLOCK_DEPRECATED_REPLACE_WITH(os_unfair_lock_unlock)
OS_NOSPIN_LOCK_AVAILABILITY
void _os_nospin_lock_unlock(_os_nospin_lock_t lock); // 解锁
#undef OSSpinLockUnlock // 解除上面的原始 API 的解锁的宏定义
#define OSSpinLockUnlock(lock) _os_nospin_lock_unlock(lock)
至此 OSSpinLockDeprecated.h 文件代码结束,整体而言只有 4 条。
OSSpinLock 的安全问题
具体来说,如果一个低优先级的线程获得锁并访问共享资源,这时一个高优先级的线程也尝试获得这个锁,它会处于 spin lock 的忙等状态从而占用大量 CPU。此时低优先级线程无法与高优先级线程争夺 CPU 时间,从而导致任务迟迟完不成、无法释放 lock。这并不只是理论上的问题,libobjc 已经遇到了很多次这个问题了,于是苹果的工程师停用了 OSSpinLock。 苹果工程师 Greg Parker 提到,对于这个问题,一种解决方案是用 truly unbounded backoff 算法,这能避免 livelock 问题,但如果系统负载高时,它仍有可能将高优先级的线程阻塞数十秒之久;另一种方案是使用 handoff lock 算法,这也是 libobjc 目前正在使用的。锁的持有者会把线程 ID 保存到锁内部,锁的等待者会临时贡献出它的优先级来避免优先级反转的问题。理论上这种模式会在比较复杂的多锁条件下产生问题,但实践上目前还一切都好。 libobjc 里用的是 Mach 内核的 thread_switch() 然后传递了一个 mach thread port 来避免优先级反转,另外它还用了一个私有的参数选项,所以开发者无法自己实现这个锁。另一方面,由于二进制兼容问题,OSSpinLock 也不能有改动。 最终的结论就是,除非开发者能保证访问锁的线程全部都处于同一优先级,否则 iOS 系统中所有类型的自旋锁都不能再使用了。-《不再安全的 OSSpinLock》
os_unfair_lock
os_unfair_lock 引子
看到 struct SideTable
定义中第一个成员变量是 spinlock_t slock
;, 这里展开对 spinlock_t
的学习。
struct SideTable {
spinlock_t slock;
...
};
spinlock_t
其实是使用 using
声明的一个模版类。
#if DEBUG
# define LOCKDEBUG 1
#else
# define LOCKDEBUG 0
#endif
template <bool Debug> class mutex_tt;
using spinlock_t = mutex_tt<LOCKDEBUG>;
所以 spinlock_t
其实是一个互斥锁,与它的名字自旋锁是不符的,其实以前它是OSSpinLock
,因为其优先级反转导致的安全问题而被遗弃了。
template <bool Debug>
class mutex_tt : nocopy_t { // 继承自 nocopy_t
os_unfair_lock mLock;
public:
constexpr mutex_tt() : mLock(OS_UNFAIR_LOCK_INIT) {
lockdebug_remember_mutex(this);
}
...
};
nocopy_t
正如其名,删除编译器默认生成的复制构造函数和赋值操作符,而构造函数和析构函数则依然使用编译器默认生成的。
// Mix-in for classes that must not be copied.
// 构造函数 和 析构函数 使用编译器默认生成的,删除 复制构造函数 和 赋值操作符。
class nocopy_t {
private:
nocopy_t(const nocopy_t&) = delete;
const nocopy_t& operator=(const nocopy_t&) = delete;
protected:
constexpr nocopy_t() = default;
~nocopy_t() = default;
};
mute_tt
类的第一个成员变量是: os_unfair_lock mLock
。
os_unfair_lock 正片
在 usr/include/os/lock.h
中看到 os_unfair_lock
的定义,使用 os_unfair_lock
首先需要引入 #import <os/lock.h>
。
os_unfair_lock API 简单使用
os_unfair_lock API
很简单,首先看下使用示例。
#import "ViewController.h"
#import <os/lock.h> // os_unfair_lock
@interface ViewController ()
@property (nonatomic, assign) NSInteger sum;
@property (nonatomic, assign) os_unfair_lock unfairL;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
_sum = 100; // 只是给自动生成的 _sum 成员变量赋值,不会调用 sum 的 setter 函数
self->_sum = 1000; // 只是给自动生成的 _sum 成员变量赋值,不会调用 sum 的 setter 函数
// 一定要区分 self. 中的 .,它和 C/C++ 中的 . 是不一样的,OC 中的 . 是调用 getter/setter 函数。
// 开始一直疑惑 self.xxx,self 是一个指针,不是应该使用 self->xxx 吗?
// 在 OC 中,应该是 self->_xxx,_xxx 是 xxx 属性自动生成的对应的成员变量 _xxx
// self 是一个结构体指针,所以访问指针的成员变量,只能是 self->_xxx,不能是 self->xxx
// 等号左边的 "self.unfairL = xxx" 相当于调用 unfairL 的 setter 函数给它赋值
// 即 [self setUnfairL:OS_UNFAIR_LOCK_INIT];
// 等号右边的 "xxx = self.unfaiL" 或者 "self.unfairL" 的使用,
// 相当于调用 unfairL 的 getter 函数,读取它的值
// 相当于调用 getter 函数:[self unfairL]
/*
// os_unfair_lock 是一个结构体
typedef struct os_unfair_lock_s {
uint32_t _os_unfair_lock_opaque;
} os_unfair_lock, *os_unfair_lock_t;
*/
self.unfairL = OS_UNFAIR_LOCK_INIT; // 初始化
dispatch_queue_t globalQueue_DEFAULT = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
self.sum = 0;
__weak typeof(self) _self = self;
dispatch_async(globalQueue_DEFAULT, ^{
__strong typeof(_self) self = _self;
// 不是使用 &self.unfairL,
// 这样使用相当于 &[self unfairL]
// 不能这样取地址
// &[self unfairL],
// 报错: Cannot take the address of an rvalue of type 'os_unfair_lock'
// 报错: 不能获取类型为 "os_unfair_lock" 的右值的地址
// &self.unfairL;
// 报错: Address of property expression requested
// 只能使用 &self->_unfairL
// 先拿到成员变量 _unfairL,然后再取地址
os_unfair_lock_lock(&self->_unfairL); // 加锁
// os_unfair_lock_lock(&self->_unfairL); // 重复加锁会直接 crash
for (unsigned int i = 0; i < 10000; ++i) {
self.sum++;
}
os_unfair_lock_unlock(&self->_unfairL); // 解锁
NSLog(@"⏰⏰⏰ %ld", self.sum);
});
dispatch_async(globalQueue_DEFAULT, ^{
__strong typeof(_self) self = _self;
os_unfair_lock_lock(&self->_unfairL); // 加锁
for (unsigned int i = 0; i < 10000; ++i) {
self.sum++;
}
os_unfair_lock_unlock(&self->_unfairL); // 解锁
NSLog(@"⚽️⚽️⚽️ %ld", self.sum);
});
}
@end
// 打印:
⚽️⚽️⚽️ 10000
⏰⏰⏰ 20000
lock.h 文件内容
首先是一个宏定义告诉我们 os_unfair_lock
出现的时机。看到os_unfair_lock
是在iOS 10.0
以后首次出现的。
#define OS_LOCK_API_VERSION 20160309
#define OS_UNFAIR_LOCK_AVAILABILITY \
__API_AVAILABLE(macos(10.12), ios(10.0), tvos(10.0), watchos(3.0))
/*!
@typedef os_unfair_lock
@abstract
Low-level lock that allows waiters to block efficiently on contention.
In general, higher level synchronization primitives such as those provided by
the pthread or dispatch subsystems should be preferred.
The values stored in the lock should be considered opaque and implementation
defined, they contain thread ownership information that the system may use
to attempt to resolve priority inversions.
This lock must be unlocked from the same thread that locked it, attempts to
unlock from a different thread will cause an assertion aborting the process.
This lock must not be accessed from multiple processes or threads via shared
or multiply-mapped memory, the lock implementation relies on the address of
the lock value and owning process.
Must be initialized with OS_UNFAIR_LOCK_INIT
@discussion
Replacement for the deprecated OSSpinLock. Does not spin on contention but
waits in the kernel to be woken up by an unlock.
As with OSSpinLock there is no attempt at fairness or lock ordering, e.g. an
unlocker can potentially immediately reacquire the lock before a woken up
waiter gets an opportunity to attempt to acquire the lock. This may be
advantageous for performance reasons, but also makes starvation of waiters a
possibility.
*/
对以上摘要内容进行总结,大概包括以下 几 点:
os_unfair_lock_s
结构,typedef 定义别名,os_unfair_lock
是一个os_unfair_lock_s
结构体,os_unfair_lock_t
是一个 os_unfair_lock_s
指针,该结构体内部就一个uint32_t _os_unfair_lock_opaque
成员变量。
OS_UNFAIR_LOCK_AVAILABILITY
typedef struct os_unfair_lock_s {
uint32_t _os_unfair_lock_opaque;
} os_unfair_lock, *os_unfair_lock_t;
针对不同的平台或者 C++
版本以不同的方式来进行初始化 (os_unfair_lock){0}
。
1.(os_unfair_lock){0}
2.os_unfair_lock{}
3.os_unfair_lock()
4.{0}
#ifndef OS_UNFAIR_LOCK_INIT
#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L
#define OS_UNFAIR_LOCK_INIT ((os_unfair_lock){0}) // ⬅️
#elif defined(__cplusplus) && __cplusplus >= 201103L
#define OS_UNFAIR_LOCK_INIT (os_unfair_lock{}) // ⬅️
#elif defined(__cplusplus)
#define OS_UNFAIR_LOCK_INIT (os_unfair_lock()) // ⬅️
#else
#define OS_UNFAIR_LOCK_INIT {0} // ⬅️
#endif
#endif // OS_UNFAIR_LOCK_INIT
- os_unfair_lock_lock 加锁。
/*!
* @function os_unfair_lock_lock
*
* @abstract
* Locks an os_unfair_lock. // 锁定一个 os_unfair_lock
*
* @param lock
* Pointer to an os_unfair_lock. // 参数是一个 os_unfair_lock 指针
*/
OS_UNFAIR_LOCK_AVAILABILITY
OS_EXPORT OS_NOTHROW OS_NONNULL_ALL
void os_unfair_lock_lock(os_unfair_lock_t lock);
-
os_unfair_lock_trylock
尝试加锁。
/*!
* @function os_unfair_lock_trylock
*
* @abstract
* Locks an os_unfair_lock if it is not already locked.
* 锁定一个 os_unfair_lock,如果它是之前尚未锁定的。
*
* @discussion
* It is invalid to surround this function with a retry loop, if this function
* returns false, the program must be able to proceed without having acquired
* the lock, or it must call os_unfair_lock_lock() directly (a retry loop around
* os_unfair_lock_trylock() amounts to an inefficient implementation of
* os_unfair_lock_lock() that hides the lock waiter from the system and prevents
* resolution of priority inversions).
* 如果此函数返回 false,则用重试循环包围此函数是无效的,程序必须能够有能力处理这种没有获得锁的情况保证程序正常运行,
* 或者必须直接调用 os_unfair_lock_lock()(os_unfair_lock_lock 会使线程阻塞一直到获得锁为止)。
* (围绕 os_unfair_lock_trylock() 的重试循环等于 os_unfair_lock_lock() 的低效实现,
* 该实现将 lock waiter 从系统中隐藏并解决了优先级反转问题)
*
* @param lock
* Pointer to an os_unfair_lock.
* 参数是一个指向 os_unfair_lock 的指针。
*
* @result
* Returns true if the lock was succesfully locked and false if the lock was already locked.
* 锁定成功返回 true,如果之前已经被锁定则返回 false。
*
*/
OS_UNFAIR_LOCK_AVAILABILITY
OS_EXPORT OS_NOTHROW OS_WARN_RESULT OS_NONNULL_ALL
bool os_unfair_lock_trylock(os_unfair_lock_t lock);
-
os_unfair_lock_unlock
解锁。
/*!
* @function os_unfair_lock_unlock
*
* @abstract
* Unlocks an os_unfair_lock. // 解锁
*
* @param lock
* Pointer to an os_unfair_lock.
*/
OS_UNFAIR_LOCK_AVAILABILITY
OS_EXPORT OS_NOTHROW OS_NONNULL_ALL
void os_unfair_lock_unlock(os_unfair_lock_t lock);
-
os_unfair_lock_assert_owner
判断当前线程是否是os_unfair_lock
的所有者,否则触发断言。
/*!
* @function os_unfair_lock_assert_owner
*
* @abstract
* Asserts that the calling thread is the current owner of the specified unfair lock.
*
* @discussion
* If the lock is currently owned by the calling thread, this function returns.
* 如果锁当前由调用线程所拥有,则此函数正常执行返回。
*
* If the lock is unlocked or owned by a different thread, this function asserts and terminates the process.
* 如果锁是未锁定或者由另一个线程所拥有,则执行断言。
*
* @param lock
* Pointer to an os_unfair_lock.
*/
OS_UNFAIR_LOCK_AVAILABILITY
OS_EXPORT OS_NOTHROW OS_NONNULL_ALL
void os_unfair_lock_assert_owner(os_unfair_lock_t lock);
-
os_unfair_lock_assert_not_owner
与上相反,如果当前线程是指定os_unfair_lock
的所有者则触发断言。
/*!
* @function os_unfair_lock_assert_not_owner
*
* @abstract
* Asserts that the calling thread is not the current owner of the specified unfair lock.
*
* @discussion
* If the lock is unlocked or owned by a different thread, this function returns.
*
* If the lock is currently owned by the current thread, this function assertsand terminates the process.
*
* @param lock
* Pointer to an os_unfair_lock.
*/
OS_UNFAIR_LOCK_AVAILABILITY
OS_EXPORT OS_NOTHROW OS_NONNULL_ALL
void os_unfair_lock_assert_not_owner(os_unfair_lock_t lock);
- 测试
os_unfair_lock_assert_owner
和os_unfair_lock_assert_not_owner
。
dispatch_async(globalQueue_DEFAULT, ^{
os_unfair_lock_assert_owner(&self->_unfairL);
});
os_unfair_lock_assert_not_owner(&self->_unfairL);
pthread_mutex_t
pthread_mutex_t
是 C 语言下多线程互斥锁的方式,是跨平台使用的锁,等待锁的线程会处于休眠状态,可根据不同的属性配置把pthread_mutex_t
初始化为不同类型的锁,例如:互斥锁、递归锁、条件锁。当使用递归锁时,允许同一个线程重复进行加锁,另一个线程访问时就会等待,这样可以保证多线程时访问共用资源的安全性。pthread_mutex_t
使用时首先要引入头文件 #import <pthread.h>
。
PTHREAD_MUTEX_NORMAL // 缺省类型,也就是普通类型,当一个线程加锁后,其余请求锁的线程将形成一个队列,并在解锁后先进先出原则获得锁。
PTHREAD_MUTEX_ERRORCHECK // 检错锁,如果同一个线程请求同一个锁,则返回 EDEADLK,否则与普通锁类型动作相同。这样就保证当不允许多次加锁时不会出现嵌套情况下的死锁
PTHREAD_MUTEX_RECURSIVE //递归锁,允许同一个线程对同一锁成功获得多次,并通过多次 unlock 解锁。
PTHREAD_MUTEX_DEFAULT // 适应锁,动作最简单的锁类型,仅等待解锁后重新竞争,没有等待队列。
pthread_mutex_trylock
和 trylock
不同,trylock
返回的是 YES
和 NO
,pthread_mutex_trylock
加锁成功返回的是0
,失败返回的是错误提示码。
pthread_mutex_t 简单使用
pthread_mutex_t
初始化时使用不同的 pthread_mutexattr_t
可获得不同类型的锁。
互斥锁( PTHREAD_MUTEX_DEFAULT 或 PTHREAD_MUTEX_NORMAL )
#import "ViewController.h"
#import <pthread.h> // pthread_mutex_t
@interface ViewController ()
@property (nonatomic, assign) NSInteger sum;
@property (nonatomic, assign) pthread_mutex_t lock;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.sum = 0;
dispatch_queue_t global_queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
// 1. 互斥锁,默认状态为互斥锁
// 初始化属性
pthread_mutexattr_t att;
pthread_mutexattr_init(&att);
// 设置属性,描述锁是什么类型
pthread_mutexattr_settype(&att, PTHREAD_MUTEX_DEFAULT);
// 初始化锁
pthread_mutex_init(&self->_lock, &att);
// 销毁属性
pthread_mutexattr_destroy(&att);
__weak typeof(self) _self = self;
dispatch_async(global_queue, ^{
__strong typeof(_self) self = _self;
if (!self) return;
pthread_mutex_lock(&self->_lock);
for (unsigned int i = 0; i < 10000; ++i) {
self.sum++;
}
pthread_mutex_unlock(&self->_lock);
NSLog(@"??? %ld", (long)self.sum);
});
dispatch_async(global_queue, ^{
__strong typeof(_self) self = _self;
if (!self) return;
pthread_mutex_lock(&self->_lock);
for (unsigned int i = 0; i < 10000; ++i) {
self.sum++;
}
pthread_mutex_unlock(&self->_lock);
NSLog(@"??? %ld", (long)self.sum);
});
}
#pragma mark - dealloc
- (void)dealloc {
NSLog(@"?????? dealloc 同时释放?...");
// 销毁锁
pthread_mutex_destroy(&self->_lock);
}
@end
// 打印 ?️:
??? 10000
??? 20000
?????? dealloc 同时释放?...
递归锁( PTHREAD_MUTEX_RECURSIVE )
#import "ViewController.h"
#import <pthread.h> // pthread_mutex_t
static int count = 3;
@interface ViewController ()
@property (nonatomic, assign) NSInteger sum;
@property (nonatomic, assign) pthread_mutex_t recursivelock;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.sum = 0;
dispatch_queue_t global_queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
// 2. 递归锁(PTHREAD_MUTEX_RECURSIVE)
pthread_mutexattr_t recursiveAtt;
pthread_mutexattr_init(&recursiveAtt);
// 设置属性,描述锁是什么类型
pthread_mutexattr_settype(&recursiveAtt, PTHREAD_MUTEX_RECURSIVE);
// 初始化锁
pthread_mutex_init(&self->_recursivelock, &recursiveAtt);
// 销毁属性
pthread_mutexattr_destroy(&recursiveAtt);
__weak typeof(self) _self = self;
dispatch_async(global_queue, ^{
__strong typeof(_self) self = _self;
if (!self) return;
pthread_mutex_lock(&self->_recursivelock);
for (unsigned int i = 0; i < 10000; ++i) {
self.sum++;
}
pthread_mutex_unlock(&self->_recursivelock);
NSLog(@"??? %ld", (long)self.sum);
});
dispatch_async(global_queue, ^{
__strong typeof(_self) self = _self;
if (!self) return;
// 递归锁验证
[self recursiveAction];
});
dispatch_async(global_queue, ^{
__strong typeof(_self) self = _self;
if (!self) return;
pthread_mutex_lock(&self->_recursivelock);
for (unsigned int i = 0; i < 10000; ++i) {
self.sum++;
}
pthread_mutex_lock(&self->_recursivelock);
NSLog(@"??? %ld", (long)self.sum);
});
dispatch_async(global_queue, ^{
__strong typeof(_self) self = _self;
if (!self) return;
// 递归锁验证
[self recursiveAction];
});
}
#pragma mark - Private Methods
- (void)recursiveAction {
pthread_mutex_lock(&self->_recursivelock);
NSLog(@"??? count = %d", count);
if (count > 0) {
count--;
[self recursiveAction];
}
// else { // 如果是单线程的话,这里加一个递归出口没有任何问题
// return;
// }
pthread_mutex_unlock(&self->_recursivelock);
count = 3;
}
#pragma mark - dealloc
- (void)dealloc {
NSLog(@"?????? dealloc 同时释放?...");
pthread_mutex_destroy(&self->_recursivelock);
}
@end
// 打印 ?️:
??? 10000
??? count = 3
??? count = 2
??? count = 1
??? count = 0
??? 20000
??? count = 3
??? count = 2
??? count = 1
??? count = 0
?????? dealloc 同时释放?...
条件锁
首先设定以下场景,两条线程A
和B
,A
线程中执行删除数组元素,B
线程中执行添加数组元素,由于不知道哪个线程会先执行,所以需要加锁实现,只有在添加之后才能执行删除操作,为互斥锁添加条件可以实现。通过此方法可以实现线程依赖。
#import "ViewController.h"
#import <pthread.h> // pthread_mutex_t
@interface ViewController ()
@property (nonatomic, strong) NSMutableArray *dataArr;
@property (nonatomic, assign) pthread_mutex_t lock;
@property (nonatomic, assign) pthread_cond_t condition;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// 初始化数组
self.dataArr = [NSMutableArray array];
// 初始化锁
pthread_mutexattr_t att;
pthread_mutexattr_init(&att);
pthread_mutexattr_settype(&att, PTHREAD_MUTEX_DEFAULT);
pthread_mutex_init(&self->_lock, &att);
pthread_mutexattr_destroy(&att);
// 初始化条件
pthread_cond_init(&self->_condition, NULL);
dispatch_queue_t global_DEFAULT = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_queue_t global_HIGH = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);
__weak typeof(self) _self = self;
dispatch_async(global_HIGH, ^{
__strong typeof(_self) self = _self;
if (!self) return;
pthread_mutex_lock(&self->_lock);
NSLog(@"?????? delete begin");
if (self.dataArr.count < 1) {
pthread_cond_wait(&self->_condition, &self->_lock);
}
[self.dataArr removeLastObject];
NSLog(@"数组执行删除元素操作");
pthread_mutex_unlock(&self->_lock);
});
dispatch_async(global_DEFAULT, ^{
__strong typeof(_self) self = _self;
if (!self) return;
pthread_mutex_lock(&self->_lock);
NSLog(@"?????? add begin");
[self.dataArr addObject:@"CHM"];
pthread_cond_signal(&self->_condition);
NSLog(@"数组执行添加元素操作");
pthread_mutex_unlock(&self->_lock);
});
NSThread *deThread = [[NSThread alloc] initWithTarget:self selector:@selector(deleteObj) object:nil];
[deThread start];
// sleep 1 秒,确保删除元素的线程先获得锁
sleep(1);
NSThread *addThread = [[NSThread alloc] initWithTarget:self selector:@selector(addObj) object:nil];
[addThread start];
}
#pragma mark - Private Methods
- (void)deleteObj {
pthread_mutex_lock(&self->_lock);
NSLog(@"?????? delete begin");
// 添加判断,如果没有数据则添加条件
if (self.dataArr.count < 1) {
// 添加条件,如果数组为空,则添加等待线程休眠,将锁让出,这里会将锁让出去,所以下面的 addObj 线程才能获得锁
// 接收到信号时会再次加锁,然后继续向下执行
pthread_cond_wait(&self->_condition, &self->_lock);
}
[self.dataArr removeLastObject];
NSLog(@"数组执行删除元素操作");
pthread_mutex_unlock(&self->_lock);
}
- (void)addObj {
pthread_mutex_lock(&self->_lock);
NSLog(@"?????? add begin");
[self.dataArr addObject:@"HTI"];
// 发送信号,说明已经添加元素了
pthread_cond_signal(&self->_condition);
NSLog(@"数组执行添加元素操作");
pthread_mutex_unlock(&self->_lock);
}
#pragma mark - dealloc
- (void)dealloc {
NSLog(@"?????? dealloc 同时释放?...");
pthread_mutex_destroy(&self->_lock);
pthread_cond_destroy(&self->_condition);
}
@end
// 打印 ?️:
?????? delete begin
?????? add begin
数组执行添加元素操作
数组执行删除元素操作
?????? dealloc 同时释放?...
NSLock
1.基于 mutex 基本锁的封装,更加面向对象,等待锁的线程会处于休眠状态。
2.遵守 NSLocking 协议,NSLocking 协议中仅有两个方法 -(void)lock 和 -(void)unlock。
3.可能会用到的方法:
4.初始化跟其他 OC 对象一样,直接进行 alloc 和 init 操作。
5.-(void)lock; 加锁。
6.-(void)unlock; 解锁。
7.-(BOOL)tryLock; 尝试加锁。
8.-(BOOL)lockBeforeDate:(NSDate *)limit; 在某一个时间点之前等待加锁。
9.在主线程连续调用 [self.lock lock] 会导致主线程死锁。
10.在主线程没有获取 Lock 的情况下和在获取 Lock 的情况下,连续两次 [self.lock unlock] 都不会发生异常。(其他的锁可能连续解锁的情况下会导致 crash,还没有来的及测试)
11.在子线程连续 [self.lock lock] 会导致死锁,同时别的子线获取 self.lock 则会一直等待下去。
12.同时子线程死锁会导致 ViewController 不释放。
NSLock 使用
@interface ViewController ()
@property (nonatomic, assign) NSInteger sum;
@property (nonatomic, strong) NSLock *lock;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.sum = 0;
self.lock = [[NSLock alloc] init];
dispatch_queue_t global_queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
__weak typeof(self) _self = self;
dispatch_async(global_queue, ^{
__strong typeof(_self) self = _self;
if (!self) return;
// 如果此处加锁失败,则阻塞当前线程,下面的代码不会执行,
// 直到等到 lock 被其他线程释放了,它可以加锁了,才会接着执行下面的代码
[self.lock lock];
for (unsigned int i = 0; i < 10000; ++i) {
self.sum++;
}
[self.lock unlock];
NSLog(@"??? %ld", (long)self.sum);
});
dispatch_async(global_queue, ^{
__strong typeof(_self) self = _self;
if (!self) return;
// 如果此处加锁失败,则阻塞当前线程,下面的代码不会执行,
// 直到等到 lock 被其他线程释放了,它可以加锁了,才会接着执行下面的代码
[self.lock lock];
for (unsigned int i = 0; i < 10000; ++i) {
self.sum++;
}
[self.lock unlock];
NSLog(@"??? %ld", (long)self.sum);
});
}
#pragma mark - dealloc
- (void)dealloc {
NSLog(@"?????? dealloc 同时释放?...");
}
@end
// 打印结果:
??? 20000
??? 10000
?????? dealloc 同时释放?...
__weak typeof(self) _self = self;
// 线程 1
dispatch_async(global_queue, ^{
__strong typeof(_self) self = _self;
if (!self) return;
[self.lock lock];
for (unsigned int i = 0; i < 10000; ++i) {
self.sum++;
}
sleep(3);
[self.lock unlock];
NSLog(@"??? %ld", (long)self.sum);
});
// 线程 2
dispatch_async(global_queue, ^{
__strong typeof(_self) self = _self;
if (!self) return;
sleep(1); // 保证让线程 1 先获得锁
// 如果此处用 1,则在这个时间点不能获得锁
// 如果是用大于 2 的数字,则能获得锁
// 且这个 if 函数是会阻塞当前线程的
if ([self.lock lockBeforeDate: [NSDate dateWithTimeIntervalSinceNow:1]]) {
for (unsigned int i = 0; i < 10000; ++i) {
self.sum++;
}
[self.lock unlock];
} else {
NSLog(@"lockBeforeDate 失败,会直接来到这里吗,会不阻塞当前线程吗?");
}
NSLog(@"??? %ld", (long)self.sum);
});
[self.lock lockBeforeDate: [NSDate dateWithTimeIntervalSinceNow:1]]
,lockBeforeDate: 方法会在指定 Date 之前尝试加锁,且这个过程是会阻塞线程 2 的,如果在指定时间之前都不能加锁,则返回 false,在指定时间之前能加锁,则返回 true。
_priv 和 name,检测各个阶段,_priv 一直是 NULL。name 是用来标识的,用来输出的时候作为 lock 的名称。如果是三个线程,那么一个线程在加锁的时候,其余请求锁的的线程将形成一个等待队列,按先进先出原则,这个结果可以通过修改线程优先级进行测试得出。
NSRecursiveLock
1.同 NSLock 一样,也是基于 mutex 的封装,不过是基于 mutex 递归锁的封装,所以这是一个递归锁。
2.遵守 NSLocking 协议,NSLocking 协议中仅有两个方法 -(void)lock 和 -(void)unlock。
3.可能会用到的方法:
4.继承自 NSObject,所以初始化跟其他 OC 对象一样,直接进行 alloc 和 init 操作。
5.-(void)lock; 加锁
6.-(void)unlock; 解锁
7.-(BOOL)tryLock; 尝试加锁
8-(BOOL)lockBeforeDate:(NSDate *)limit; 在某一个时间点之前等待加锁。
9.递归锁是可以在同一线程连续调用 lock 不会直接导致阻塞死锁,但是依然要执行相等次数的 unlock。不然异步线程再获取该递归锁会导致该异步线程阻塞死锁。
10.递归锁允许同一线程多次加锁,不同线程进入加锁入口会处于等待状态,需要等待上一个线程解锁完成才能进入加锁状态。
NSRecursiveLock 使用
其实是实现上面 pthread_mutex_t
和 PTHREAD_MUTEX_RECURSIVE
完成的递归锁场景,只是这里使用 NSRecursiveLock API
更加精简,使用起来更加简单方便。
#import "ViewController.h"
static int count = 3;
@interface ViewController ()
@property (nonatomic, assign) NSInteger sum;
@property (nonatomic, strong) NSLock *lock;
@property (nonatomic, strong) NSRecursiveLock *recursiveLock;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
self.sum = 0;
self.recursiveLock = [[NSRecursiveLock alloc] init];
dispatch_queue_t global_queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
__weak typeof(self) _self = self;
dispatch_async(global_queue, ^{
__strong typeof(_self) self = _self;
if (!self) return;
[self.recursiveLock lock];
for (unsigned int i = 0; i < 10000; ++i) {
self.sum++;
}
[self.recursiveLock unlock];
NSLog(@"??? %ld", (long)self.sum);
});
dispatch_async(global_queue, ^{
__strong typeof(_self) self = _self;
if (!self) return;
[self recursiveAction];
});
dispatch_async(global_queue, ^{
__strong typeof(_self) self = _self;
if (!self) return;
[self.recursiveLock lock];
for (unsigned int i = 0; i < 10000; ++i) {
self.sum++;
}
[self.recursiveLock unlock];
NSLog(@"??? %ld", (long)self.sum);
});
dispatch_async(global_queue, ^{
__strong typeof(_self) self = _self;
if (!self) return;
[self recursiveAction];
});
}
#pragma mark - Private Methods
- (void)recursiveAction {
[self.recursiveLock lock];
NSLog(@"??? count = %d", count);
if (count > 0) {
count--;
[self recursiveAction];
}
// else { // 如果是单线程的话,这里加一个递归出口没有任何问题
// return;
// }
[self.recursiveLock unlock];
count = 3;
}
#pragma mark - dealloc
- (void)dealloc {
NSLog(@"?????? dealloc 同时释放?...");
}
@end
// 打印结果:
??? count = 3
??? 10000
??? count = 2
??? count = 1
??? count = 0
??? 20000
??? count = 3
??? count = 2
??? count = 1
??? count = 0
NSRecursiveLock *lock = [[NSRecursiveLock alloc] init];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
static void (^RecursieveBlock)(int);
RecursiveBlock = ^(int value) {
[lock lock];
if (value > 0) {
NSLog(@"value: %d", value);
RecursiveBlock(value - 1);
}
[lock unlock];
};
RecursiveBlock(2);
});
如上示例,如果用 NSLock 的话,lock 先锁上,但未执行解锁的时候,就会进入递归的下一层,并再次请求上锁,阻塞了该线程,线程被阻塞了,自然后面的解锁代码就永远不会执行,而形成了死锁。而 NSRecursiveLock 递归锁就是为了解决这个问题。
NSCondition
1.基于 mutex
基础锁和 cont
条件的封装,所以它是互斥锁且自带条件,等待锁的线程休眠。
2.遵守NSLocking
协议,NSLocking
协议中仅有两个方法 -(void)lock
和-(void)unlock
。
3.可能会用到的方法
4.初始化跟其它 OC 对象一样,直接进行alloc
和 init
操作。
5.-(void)lock;
加锁
6.-(void)unlock;
解锁
7.-(BOOL)tryLock;
尝试加锁
8.-(BOOL)lockBeforeDate:(NSDate *)limit;
在某一个时间点之前等待加锁
9.-(void)wait;
等待条件(进入休眠的同时放开锁,被唤醒的同时再次加锁)
10.-(void)signal;
发送信号激活等待该条件的线程,切记线程收到后是从 wait 状态开始的
11.- (void)broadcast;
发送广播信号激活等待该条件的所有线程,切记线程收到后是从 wait 状态开始的
NSCondition 使用
#import "ViewController.h"
@interface ViewController ()
@property (nonatomic, strong) NSMutableArray *dataArr;
@property (nonatomic, strong) NSCondition *condition;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
// 初始化数组
self.dataArr = [NSMutableArray array];
self.condition = [[NSCondition alloc] init];
dispatch_queue_t global_queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
__weak typeof(self) _self = self;
dispatch_async(global_queue, ^{
__strong typeof(_self) self = _self;
if (!self) return;
[self deleteObj];
});
dispatch_async(global_queue, ^{
__strong typeof(_self) self = _self;
if (!self) return;
[self deleteObj];
});
// sleep 0.5 秒,确保删除元素的操作先取得锁
sleep(0.5);
dispatch_async(global_queue, ^{
__strong typeof(_self) self = _self;
if (!self) return;
[self addObj];
});
}
#pragma mark - Private Methods
- (void)deleteObj {
[self.condition lock];
NSLog(@"?????? delete begin");
// 添加判断,如果没有数据则添加条件
if (self.dataArr.count < 1) {
// 添加条件,如果数组为空,则添加等待线程休眠,将锁让出,这里会将锁让出去,所以下面的 addObj 线程才能获得锁
// 接收到信号时会再次加锁,然后继续向下执行
NSLog(@"下面是进入 wait...");
[self.condition wait];
// 当 broadcast 过来的时候还是继续往下执行,
// 切记不是从 deleteObj 函数头部开始的,是从这里开始的
// 所以当第一个异步删除数组元素后,第二个异步进来时数组已经空了
NSLog(@"接收到 broadcast 或 signal 后的函数起点");
}
NSLog(@"%@", self.dataArr);
[self.dataArr removeLastObject];
NSLog(@"?????? 数组执行删除元素操作");
[self.condition unlock];
}
- (void)addObj {
[self.condition lock];
NSLog(@"?????? add begin");
[self.dataArr addObject:@"CHM"];
// 发送信号,说明已经添加元素了
// [self.condition signal];
// 通知所有符合条件的线程
[self.condition broadcast];
NSLog(@"?????? 数组执行添加元素操作");
[self.condition unlock];
}
#pragma mark - dealloc
- (void)dealloc {
NSLog(@"?????? dealloc 同时释放?...");
}
@end
// 打印结果:
// 这里两个异步线程执行都 [self.condition lock],都正常进入了,
// 并没有因为 self.condition 先被一条线程获取加锁了而另一条线程处于阻塞等待状态,
?????? delete begin
下面是进入 wait...
?????? delete begin
下面是进入 wait...
?????? add begin
?????? 数组执行添加元素操作
接收到 broadcast 或 signal 后的函数起点
(
CHM
)
?????? 数组执行删除元素操作
接收到 broadcast 或 signal 后的函数起点
(
)
?????? 数组执行删除元素操作
?????? dealloc 同时释放?...
NSConditionLock
1.基于 NSCondition
的进一步封装,可以更加高级的设置条件值。
2.遵守 NSLocking 协议,NSLocking 协议中仅有两个方法 -(void)lock 和 -(void)unlock。
3.可能用到的方法:
4.初始化跟其他 OC 对象一样,直接 alloc 和 initWithCondition:(NSInteger)condition 操作;(如果使用 init 方法,则 condition 默认为 0)。
5.有一个属性是 @property(readonly) NSInteger condition; 用来设置条件值,如果不设定,则默认为零。
6.-(void)lock; 直接加锁。
7.-(void)lockWhenCondition:(NSInteger)condition; 根据 condition 值加锁,如果入参和当前的 condition 不等则不加。
8.-(void)unlockWithCondition:(NSInteger)condition; 解锁, 并设定 condition 的值为入参。
9.-(BOOL)tryLock; 尝试加锁。
10.-(BOOL)lockBeforeDate:(NSDate *)limit; 在某一个时间点之前等待加锁。
NSConditionLock 使用
#import "ViewController.h"
@interface ViewController ()
@property (nonatomic, strong) NSConditionLock *lock;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.lock = [[NSConditionLock alloc] initWithCondition:0];
[self createThreads];
}
#pragma mark - Private Methods
- (void)createThreads {
// 需要执行的顺序为 A-B-C,但是因为在子线程中所以我们不能确定谁先执行,添加 sleep 使问题更突出点,c 线程先启动然后是 b 然后是 a。
NSThread *c = [[NSThread alloc] initWithTarget:self selector:@selector(threadC) object:nil];
[c start];
sleep(0.2);
NSThread *b = [[NSThread alloc] initWithTarget:self selector:@selector(threadB) object:nil];
[b start];
sleep(0.2);
NSThread *a = [[NSThread alloc] initWithTarget:self selector:@selector(threadA) object:nil];
[a start];
}
- (void)threadA {
NSLog(@"A begin");
[self.lock lockWhenCondition:0]; // 此时 Condition 值为 0 才能加锁成功,因为 Condition 初始值是 0,所以只有 A 能加锁成功
NSLog(@"A threadExcute");
[self.lock unlockWithCondition:1]; // 解锁并把 Condition 设置为 1
// [self unlock]; // 如果此处使用 unlock,则导致 B C 线程死锁,且导致 ViewController 不释放
}
- (void)threadB {
NSLog(@"B begin");
[self.lock lockWhenCondition:1]; // 此时 Condition 值为 1 才能加锁成功
NSLog(@"B threadExcute");
[self.lock unlockWithCondition:2]; // 解锁并把 Condition 设置为 2
}
- (void)threadC {
NSLog(@"C begin");
[self.lock lockWhenCondition:2]; // 此时 Condition 值为 2 才能加锁成功
NSLog(@"C threadExcute");
[self.lock unlock]; // 解锁
}
#pragma mark - dealloc
- (void)dealloc {
NSLog(@"?????? dealloc 同时释放?...");
}
// 打印结果:
// 虽然启动顺序是 C B A,但是执行顺序是 A B C,正是由 Condition 条件控制的,只有 Condition 匹配才能加锁成功,否则一直阻塞等待
C begin
B begin
A begin
A threadExcute
B threadExcute
C threadExcute
?????? dealloc 同时释放?...
1.[self.lock unlock];
执行后 condition
保持不变,依然是初始化的值或者是上次执行 lockWhenCondition:
时的值。
2.A B C 3 条线程必须都执行加锁和解锁后 ViewController
才能正常释放,除了最后一条线程可以直接使用 unlock
执行解锁外,前两条线程 unlockWithCondition:
的入参condition
的值必须和 NSConditionLock
当前的 condition
的值匹配起来。保证每条线程都lock
和unlock
,无法正常执行时都会导致线程阻塞等待,ViewController
不会释放。
3.在同一线程连续[self.lock lockWhenCondition:1];
会直接阻塞死锁,不管用的condition
是否和当前锁的 condition
相等,都会导致阻塞死锁。
NSLocking、NSLock、NSConditionLock、NSRecursiveLock、NSCondition 定义
#import <Foundation/NSObject.h>
@class NSDate;
NS_ASSUME_NONNULL_BEGIN
// NSLocking 协议,上面提到锁的类型只要是 NS 开头的都会遵守此协议
@protocol NSLocking // 看到 NSLocking 协议只有加锁和解锁两个协议方法
- (void)lock;
- (void)unlock;
@end
@interface NSLock : NSObject <NSLocking> { // NSLock 是继承自 NSObject 并遵守 NSLocking 协议
@private
void *_priv;
}
- (BOOL)tryLock; // 尝试加锁,返回 true 表示加锁成功
- (BOOL)lockBeforeDate:(NSDate *)limit; // 在某个 NSDate 之前加锁
@property (nullable, copy) NSString *name API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0));
@end
// 条件锁
@interface NSConditionLock : NSObject <NSLocking> { // 继承自 NSObject 并遵守 NSLocking 协议
@private
void *_priv;
}
- (instancetype)initWithCondition:(NSInteger)condition NS_DESIGNATED_INITIALIZER;
@property (readonly) NSInteger condition; // 只读的 condition 属性
- (void)lockWhenCondition:(NSInteger)condition; // 根据 condition 值加锁, 如果值不满足, 则不加;
- (BOOL)tryLock;
- (BOOL)tryLockWhenCondition:(NSInteger)condition;
- (void)unlockWithCondition:(NSInteger)condition; // 解锁, 并设定 condition 的值;
- (BOOL)lockBeforeDate:(NSDate *)limit; // 在某一个时间点之前等待加锁
- (BOOL)lockWhenCondition:(NSInteger)condition beforeDate:(NSDate *)limit;
@property (nullable, copy) NSString *name API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0));
@end
// 递归锁
@interface NSRecursiveLock : NSObject <NSLocking> { // 继承自 NSObject 并遵守 NSLocking 协议
@private
void *_priv;
}
- (BOOL)tryLock; // 尝试加锁,返回 true 表示加锁成功
- (BOOL)lockBeforeDate:(NSDate *)limit; // 在某个 NSDate 之前加锁
@property (nullable, copy) NSString *name API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0));
@end
API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0))
@interface NSCondition : NSObject <NSLocking> { // 继承自 NSObject 并遵守 NSLocking 协议
@private
void *_priv;
}
- (void)wait; // 添加等待,线程休眠,并将锁让出
- (BOOL)waitUntilDate:(NSDate *)limit; // 某个 NSDate
- (void)signal; // 发送信号,告知等待的线程,条件满足了
- (void)broadcast; // 通知所有符合条件的线程,(通知所有在等待的线程)
@property (nullable, copy) NSString *name API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0));
@end
NS_ASSUME_NONNULL_END