多线程的安全离不开锁的使用,常见锁的性能:
一、锁的分类
关于同步的Apple文档
基本的锁就包括了2大类:自旋锁 互斥锁
.
其他的比如条件锁
、递归锁
、信号量
都是上层的封装实现.
-
读写锁
-多
(线程)读单
(线程)写
实际是一种特殊的自旋锁
,它把对共享资源的访问者划分成读者和写者,读者只对共享资源进行读访问,写者则需要对共享资源进行写操作。读写锁相对于自旋锁而言,能提高并发性
,因为在多处理器系统中,它允许同时有多个读者来访问共享资源
,最大可能的读者数为实际的逻辑 CPU 数。写者是排他性的,一个读写锁同时只能有一个写者或多个读者
(与CPU数相关),但不能同时既有读者又有写者
。在读写锁保持期间也是抢占失效的。- 如果读写锁当前没有读者,也没有写者,那么写者可以立刻获得读写锁,否则
写者必须自旋在那里
,直到没有任何写者或读者; - 如果读写锁没有写者,那么读者可以立即获得该读写锁,否则
读者必须自旋在那里
,直到写者释放该读写锁。
- 如果读写锁当前没有读者,也没有写者,那么写者可以立刻获得读写锁,否则
1、自旋锁
- 线程反复检查锁变量是否可用。由于线程在这一过程中保持执行, 因此是一种忙等待;
- 一旦获取了自旋锁,线程会一直保持该锁,直至显式释放自旋锁;
- 自旋锁避免了进程上下文的调度开销,因此对于线程只会阻塞很短时间的场合是有效的,小而精 的任务;
- 因 一直检查询问锁是否打开可用,
耗费性能比较高
。
2、互斥锁
- 是一种多线程编程中,防止两条线程同时对同一公共资源(比如全局变量)进行读写的机制。它通过将代码切片成一个一个的临界区 而实现。
- 保证同一时间只有一条线程可进行某执行任务 - 类似保证了同步的功能。
当发现别的线程正在操作任务,当前线程获取互斥锁失败,当前线程进入休眠 (就绪状态
-等待被调度执行
) --> 一直等到其他线程打开锁之后 -->唤起 执行
。 - 常见的互斥锁 - 互斥锁分为递归和非递归锁
3.1NSLock
3.2@synchronized
3.3pthread_mutex
2.1) 递归锁
- 就是同一个线程 可以加锁 N 次而不会引发死锁。
- 常见的递归锁
2.1NSRecursiveLock
2.2pthread_mutex(recursive)
3、条件锁
- 即
条件变量
。当进程的某些资源要求不满足时就进入休眠,也就
是锁住了。当资源被分配到了,条件锁打开,进程继续运行。 - 常见的条件锁:
2.1NSCondition
2.2NSConditionLock
4、信号量 semaphore - dispatch_semaphore
- 信号量是一种更高级的
同步机制
,互斥锁可以说是
semaphore
在仅取值0/1时的特例。 - 信号量可以有更多的取值空间,用来实现更加复杂的同步,而不单单是线程间互斥。
二、锁的原理分析
以售票举例,示例代码如下:
- (void)my_lockDemo {
dispatch_async(dispatch_get_global_queue(0, 0), ^{
for (int i = 0; i < 5; i++) {
[self saleTicket];
}
});
dispatch_async(dispatch_get_global_queue(0, 0), ^{
for (int i = 0; i < 5; i++) {
[self saleTicket];
}
});
dispatch_async(dispatch_get_global_queue(0, 0), ^{
for (int i = 0; i < 3; i++) {
[self saleTicket];
}
});
dispatch_async(dispatch_get_global_queue(0, 0), ^{
for (int i = 0; i < 10; i++) {
[self saleTicket];
}
});
}
- (void)saleTicket {
if (self.ticketCount > 0) {
self.ticketCount--;
sleep(0.1);
NSLog(@"当前余票还剩:%ld张",self.ticketCount);
}else{
NSLog(@"当前车票已售罄");
}
}
运行程序,输出结果如下:
/**
当前余票还剩:17张
当前余票还剩:18张
当前余票还剩:16张
当前余票还剩:18张
当前余票还剩:13张
当前余票还剩:13张
当前余票还剩:13张
当前余票还剩:12张
... 更多打印不必贴全 ...
*/
由上示例,余票票数是有问题的,对于多线程操作,数据的安全性必须考虑。
1、@synchronized
原理
给上面的示例代码添加@synchronized
锁如下,再次运行工程:
@synchronized (self) {
if (self.ticketCount > 0) {
self.ticketCount--;
sleep(0.1);
NSLog(@"当前余票还剩:%ld张",self.ticketCount);
}else{
NSLog(@"当前车票已售罄");
}
}
/** 打印输出结果如下:
当前余票还剩:19张
当前余票还剩:18张
当前余票还剩:17张
当前余票还剩:16张
当前余票还剩:15张
当前余票还剩:14张
当前余票还剩:13张
当前余票还剩:12张
... 更多打印不必贴全 ...
*/
由上,余票的数据是正确的,线程安全已解决。@synchronized
是如何实现线程安全的呢?
- 将
main.m
文件编译成cpp
:
clang -rewrite-objc -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk
/目标文件路径/main.m
-o main3.cpp
#import
#import "AppDelegate.h"
int main(int argc, char * argv[]) {
NSString * appDelegateClassName;
@autoreleasepool {
// Setup code that might create autoreleased objects goes here.
appDelegateClassName = NSStringFromClass([AppDelegate class]);
@synchronized (appDelegateClassName) {
}
}
return UIApplicationMain(argc, argv, nil, appDelegateClassName);
}
/**************** 编译后 .cpp --> @synchronized ******************/
int main(int argc, char * argv[]) {
NSString * appDelegateClassName;
/* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool;
appDelegateClassName = NSStringFromClass(((Class (*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("AppDelegate"), sel_registerName("class")));
{ // 代码块区域
id _rethrow = 0;
id _sync_obj = (id)appDelegateClassName;
objc_sync_enter(_sync_obj);
try {
struct _SYNC_EXIT { _SYNC_EXIT(id arg) : sync_exit(arg) {}
~_SYNC_EXIT() {objc_sync_exit(sync_exit);}
id sync_exit;
} _sync_exit(_sync_obj);
} catch (id e) {_rethrow = e;}
{ struct _FIN { _FIN(id reth) : rethrow(reth) {}
~_FIN() { if (rethrow) objc_exception_throw(rethrow); }
id rethrow;
} _fin_force_rethow(_rethrow);}
}
}
return UIApplicationMain(argc, argv, __null, appDelegateClassName);
}
static struct IMAGE_INFO { unsigned version; unsigned flag; } _OBJC_IMAGE_INFO = { 0, 2 };
由上文件可看到,@synchronized
主要的2行代码objc_sync_enter
和 objc_sync_exit
。
- 运行工程,打开debug汇编调试也可找到
objc_sync_enter / objc_sync_exit
,如下:
0x104e8851e <+46>: callq 0x104e8c754 ; symbol stub for: objc_sync_enter
... more info ...
0x104e885f1 <+257>: callq 0x104e8c75a ; symbol stub for: objc_sync_exit
通过汇编,跳进到objc_sync_enter
,找到其所在库:libobjc.A.dylib
1.1、@synchronized
源码分析
打开 libobjc 源码工程,全局搜索:
// 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;
}
1.1.1、@synchronized
递归互斥锁
SyncData
: --> 链表结构
typedef struct alignas(CacheLineSize) SyncData {
struct SyncData* nextData;
DisguisedPtr object;
int32_t threadCount; // number of THREADS using this block
recursive_mutex_t mutex;
} SyncData;
recursive_mutex_t
--> recursive_mutex_tt
:
template
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() // 锁住
{
lockdebug_recursive_mutex_lock(this);
os_unfair_recursive_lock_lock(&mLock);
}
void unlock() // 开锁
{
lockdebug_recursive_mutex_unlock(this);
os_unfair_recursive_lock_unlock(&mLock);
}
void forceReset()
{
lockdebug_recursive_mutex_unlock(this);
bzero(&mLock, sizeof(mLock));
mLock = os_unfair_recursive_lock OS_UNFAIR_RECURSIVE_LOCK_INIT;
}
bool tryLock()
{
if (os_unfair_recursive_lock_trylock(&mLock)) {
lockdebug_recursive_mutex_lock(this);
return true;
}
return false;
}
bool tryUnlock()
{
if (os_unfair_recursive_lock_tryunlock4objc(&mLock)) {
lockdebug_recursive_mutex_unlock(this);
return true;
}
return false;
}
void assertLocked() {
lockdebug_recursive_mutex_assert_locked(this);
}
void assertUnlocked() {
lockdebug_recursive_mutex_assert_unlocked(this);
}
};
由上源码可验证@synchronized
是个递归互斥锁。
1.1.2、@synchronized
的实现
id2data()
:
static SyncData* id2data(id object, enum usage why)
{
spinlock_t *lockp = &LOCK_FOR_OBJ(object);
SyncData **listp = &LIST_FOR_OBJ(object);
SyncData* result = NULL;
// 1、快速查找
// 支持线程 key,通过set get 存取
#if SUPPORT_DIRECT_THREAD_KEYS
// Check per-thread single-entry fast cache for matching object
// 检查每个线程 单条目快速缓存 是否匹配对象
bool fastCacheOccupied = NO;
SyncData *data = (SyncData *)tls_get_direct(SYNC_DATA_DIRECT_KEY);
if (data) {
fastCacheOccupied = YES;
if (data->object == object) {// 当前的 object 和data中的一致
// Found a match in fast cache.
uintptr_t lockCount;
result = data;
lockCount = (uintptr_t)tls_get_direct(SYNC_COUNT_DIRECT_KEY);
if (result->threadCount <= 0 || lockCount <= 0) {
_objc_fatal("id2data fastcache is buggy");
}
switch(why) {
case ACQUIRE: {// 获取
lockCount++;// lockCount锁的次数 --> 可重复被锁
tls_set_direct(SYNC_COUNT_DIRECT_KEY, (void*)lockCount);
break;
}
case RELEASE:// 释放
lockCount--;
tls_set_direct(SYNC_COUNT_DIRECT_KEY, (void*)lockCount);
if (lockCount == 0) {
// remove from fast cache
tls_set_direct(SYNC_DATA_DIRECT_KEY, NULL);
// atomic because may collide with concurrent ACQUIRE
OSAtomicDecrement32Barrier(&result->threadCount);
}
break;
case CHECK:// 检查
// do nothing
break;
}
return result;
}
}
#endif
// 2、缓存查找 - 遍历哈希list
// Check per-thread cache of already-owned locks for matching object
// 检查 已拥有锁 的每个线程缓存 是否匹配对象
SyncCache *cache = fetch_cache(NO);
/**
typedef struct SyncCache {
unsigned int allocated;
unsigned int used;
SyncCacheItem list[0];
} SyncCache;
*/
if (cache) {
unsigned int i;
for (i = 0; i < cache->used; i++) {
SyncCacheItem *item = &cache->list[i];
if (item->data->object != object) continue;
// Found a match.
result = item->data;
if (result->threadCount <= 0 || item->lockCount <= 0) {
_objc_fatal("id2data cache is buggy");
}
switch(why) {
case ACQUIRE:
item->lockCount++;
break;
case RELEASE:
item->lockCount--;
if (item->lockCount == 0) {
// remove from per-thread cache
// 从 缓存 list 中 remove
cache->list[i] = cache->list[--cache->used];
// atomic because may collide with concurrent ACQUIRE
OSAtomicDecrement32Barrier(&result->threadCount);
}
break;
case CHECK:
// do nothing
break;
}
return result;
}
}
// Thread cache didn't find anything.
// Walk in-use list looking for matching object
// Spinlock prevents multiple threads from creating multiple
// locks for the same new object.
// We could keep the nodes in some hash table if we find that there are
// more than 20 or so distinct locks active, but we don't do that now.
// 保证这块的线程安全 lock
lockp->lock();
// 3、对象所对应的链表 遍历查找
{
SyncData* p;
SyncData* firstUnused = NULL;
for (p = *listp; p != NULL; p = p->nextData) {
if ( p->object == object ) {
result = p;
// atomic because may collide with concurrent RELEASE
OSAtomicIncrement32Barrier(&result->threadCount);
goto done;
}
// 第一次进来
if ( (firstUnused == NULL) && (p->threadCount == 0) )
firstUnused = p;
}
// no SyncData currently associated with object
if ( (why == RELEASE) || (why == CHECK) )
goto done;
// an unused one was found, use it
if ( firstUnused != NULL ) {
result = firstUnused;
result->object = (objc_object *)object;
result->threadCount = 1;
goto done;
}
}
// Allocate a new SyncData and add to list.
// XXX allocating memory with a global lock held is bad practice,
// might be worth releasing the lock, allocating, and searching again.
// But since we never free these guys we won't be stuck in allocation very often.
// 没有,创建新链表插入哈希list中
posix_memalign((void **)&result, alignof(SyncData), sizeof(SyncData));
result->object = (objc_object *)object;
result->threadCount = 1;
new (&result->mutex) recursive_mutex_t(fork_unsafe_lock);// 递归自旋锁
result->nextData = *listp;
*listp = result;
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);// KVC
tls_set_direct(SYNC_COUNT_DIRECT_KEY, (void*)1);// lockCount
} else
#endif
{
// Save in thread cache 保存到哈希list
if (!cache) cache = fetch_cache(YES);
cache->list[cache->used].data = result;
cache->list[cache->used].lockCount = 1;
cache->used++;
}
}
return result;
}
由SyncData **listp = &LIST_FOR_OBJ(object);
-->LIST_FOR_OBJ()
:
#define LIST_FOR_OBJ(obj) sDataLists[obj].data
static StripedMap sDataLists;
//
struct SyncList {
SyncData *data;
spinlock_t lock;
constexpr SyncList() : data(nil), lock(fork_unsafe_lock) { }
};
// StripedMap:
template
class StripedMap {
#if TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR
enum { StripeCount = 8 };
#else
enum { StripeCount = 64 };
#endif
struct PaddedT {
T value alignas(CacheLineSize);
};
PaddedT array[StripeCount];
static unsigned int indexForPointer(const void *p) {
uintptr_t addr = reinterpret_cast(p);
// 哈希
return ((addr >> 4) ^ (addr >> 9)) % StripeCount;
}
... more info ...
}
SyncList
结构:
-
@synchronized
逻辑流程:
对象objc
的@synchronized
:- 首先在栈存空间查找,匹配上:
lockCount++
并返回; - 没有继续在全局的哈希list缓存查找,匹配到:
lockCount++
并返回; - 没有找到,则开始遍历当前对象
objc
所对应的链表遍历查,找到,goto done
,保存到缓存; - 没找到,创建结点给新链表,
4.1 若支持线程key
-SUPPORT_DIRECT_THREAD_KEYS
,通过tls_set_direct(k,value)
- 以KVC
方式保存到tls
(tls:本地局部的线程缓存);
tls
: 线程局部存储(Thread Local Storage,TLS)
:是操作系统为线
程单独提供的私有空间,通常只有有限的容量。
4.2 不支持,将新链表保存到缓存,即 开辟空间将其存到哈希list中。
- 首先在栈存空间查找,匹配上:
*
问题:
@synchronized
性能差原因由其实现原理也可知,链表的查找速度很慢,尽管做了缓存,但其速度仍是相较慢的。
为何@synchronized
性能那么低还要用它呢?
--> 使用方便,封装性高不用关心内部加解锁。
*
注意点:
@synchronized()
使用时,需要锁住的对象要注意其生命周期,一般常见的是锁self
,其原因是对象的生命是和所在的self
,并非都用self
。
--> 我们在使用@synchronized()
时考虑保证要锁住的对象其生命正常即可。
示例代码如下:
- (void)my_lock {
dispatch_semaphore_t sem = dispatch_semaphore_create(1);
for (int i=0; i<2000; i++) {
// 1. 不加锁 --> objc_release 野指针,对象多次释放 --> crash
// dispatch_async(dispatch_get_global_queue(0, 0), ^{
// _muArray = [NSMutableArray array];
// });
// 2. @synchronized (self) --> 正常
// dispatch_async(dispatch_get_global_queue(0, 0), ^{
// @synchronized (self) {
// _muArray = [NSMutableArray array];
// }
// });
// 2.1 @synchronized (_muArray) --> crash
// dispatch_async(dispatch_get_global_queue(0, 0), ^{
// @synchronized (_muArray) {// 它可能在某时刻为nil,@synchronized锁对nil不进行任何操作so锁不住
// _muArray = [NSMutableArray array];
// }
// });
// 3. 信号量 --> 正常
dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
_muArray = [NSMutableArray array];
dispatch_semaphore_signal(sem);
}
}
2、NSLock
2.1、NSLock
源码
NSLock
在Foundation
框架中,其为开源,但swift
的Foundation
开源,我们这里以swift
的Foundation
源码进行探究。
open class NSLock: NSObject, NSLocking {
internal var mutex = _MutexPointer.allocate(capacity: 1)
#if os(macOS) || os(iOS) || os(Windows)
private var timeoutCond = _ConditionVariablePointer.allocate(capacity: 1)
private var timeoutMutex = _MutexPointer.allocate(capacity: 1)
#endif
public override init() {
#if os(Windows)
InitializeSRWLock(mutex)
InitializeConditionVariable(timeoutCond)
InitializeSRWLock(timeoutMutex)
#else
pthread_mutex_init(mutex, nil)
#if os(macOS) || os(iOS)
// lock 的创建必须 init,下面代码可说明原因:
// 条件数cond 和互斥mutex 的 init 操作
pthread_cond_init(timeoutCond, nil)
pthread_mutex_init(timeoutMutex, nil)
#endif
#endif
}
// ... ... more code ... ...
open func lock() {
#if os(Windows)
AcquireSRWLockExclusive(mutex)
#else
pthread_mutex_lock(mutex)// 互斥锁 lock
#endif
}
open func unlock() {
#if os(Windows)
ReleaseSRWLockExclusive(mutex)
AcquireSRWLockExclusive(timeoutMutex)
WakeAllConditionVariable(timeoutCond)
ReleaseSRWLockExclusive(timeoutMutex)
#else
pthread_mutex_unlock(mutex)
#if os(macOS) || os(iOS)
// Wakeup any threads waiting in lock(before:)
pthread_mutex_lock(timeoutMutex) // 解锁
pthread_cond_broadcast(timeoutCond) // 广播
pthread_mutex_unlock(timeoutMutex) // 解锁
#endif
#endif
}
// ... ... more code ... ...
}
NSLock
锁的lock
和 unlock
是通过pthead
进行了一层封装了,NSLock
锁的性能次于pthread_mutex
锁一点点。
* Tip
: NSLock
- NSRecursiveLock
- @synchronized
三者使用场景
示例代码:
- (void)my_NSLock {
NSLock *lock = [[NSLock alloc]init];// 互斥锁
NSRecursiveLock *recursiveLock = [[NSRecursiveLock alloc] init];// 递归锁
CFAbsoluteTime time = CFAbsoluteTimeGetCurrent();
for (int i =0; i<100; i++) {
dispatch_async(dispatch_get_global_queue(0, 0), ^{
static void(^testMethod)(int);
// [lock lock];// 1 - crash
[recursiveLock lock];
testMethod = ^(int value) {
// 使用 @synchronized 简单方便
// @synchronized (self) {
// if (value > 0) {
// NSLog(@"当前 value = %d",value);
// testMethod(value - 1);
// }
// };
// [lock lock];// 2 - 堵死
if (value > 0) {
NSLog(@"当前 value = %d",value);
testMethod(value - 1);
}
// [lock unlock];// 1 - crash
// [lock unlock];// 2 - 堵死
[recursiveLock unlock];
};
// [lock lock];// 2 - 正常 - 但它造成的堵塞情况太严重
testMethod(10);
// [lock unlock];// 2 - 正常
});
}
}
NSRecursiveLock
递归锁和NSLock
同是基于pthread_mutex
封装,2者不同处主要在init
设置:
NSRecursiveLock
的init
类型设置标记为 pthread_mutex_recursive
:
public override init() {
super.init()
#if os(Windows)
InitializeCriticalSection(mutex)
InitializeConditionVariable(timeoutCond)
InitializeSRWLock(timeoutMutex)
#else
#if CYGWIN
var attrib : pthread_mutexattr_t? = nil
#else
var attrib = pthread_mutexattr_t()
#endif
withUnsafeMutablePointer(to: &attrib) { attrs in
pthread_mutexattr_init(attrs)
// 类型设置标记为递归锁: pthread_mutex_recursive
pthread_mutexattr_settype(attrs, Int32(PTHREAD_MUTEX_RECURSIVE))
pthread_mutex_init(mutex, attrs)
}
#if os(macOS) || os(iOS)
pthread_cond_init(timeoutCond, nil)
pthread_mutex_init(timeoutMutex, nil)
#endif
#endif
}
递归锁的使用,在循环嵌套场景中比较适用,而NSRecursiveLock
递归锁和@synchronized
锁类似,使用上个人更倾向于@synchronized
,适用便利性更强。
3、NSCondition
和 NSConditionLock
条件锁
-
NSCondition
对象实际上作为一个锁和一个线程检查器:-->生产消费者模型
- 锁的目的主要是 在检测条件时保护数据源,执行 条件引发的任务;
- 线程检查器主要是 根据条件决定是否继续运行线程,即线程是否被阻塞。
-
NSConditionLock
锁,一旦一个线程获得锁,其他线程一定等待,它的使用可以携带着条件。
3.1、NSCondition
使用
示例代码:
#pragma mark - NSCondition -
- (void)my_testConditon {
_testCondition = [[NSCondition alloc] init];
// 创建生产-消费者
for (int i = 0; i < 10; i++) {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
[self my_producer];
});
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
[self my_consumer];
});
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
[self my_consumer];
});
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
[self my_producer];
});
}
}
- (void)my_producer {
[_testCondition lock];
self.ticketCount = self.ticketCount + 1;
NSLog(@"生产一个 现有 count %zd",self.ticketCount);
[_testCondition signal];
[_testCondition unlock];
}
- (void)my_consumer {
// 线程安全
[_testCondition lock];
// while (self.ticketCount == 0) {
// NSLog(@"等待 count %zd",self.ticketCount);
// }
if (self.ticketCount == 0) {// 这里 if 是截不住条件的
NSLog(@"等待 count %zd",self.ticketCount);
// 保证正常流程
[_testCondition wait];
}
//注意消费行为,要在等待条件判断之后
self.ticketCount -= 1;
NSLog(@"消费一个 还剩 count %zd ",self.ticketCount);
[_testCondition unlock];
}
3.2、NSConditionLock
条件锁的使用
示例代码:
- (void)my_testConditonLock {
NSConditionLock *conditionLock = [[NSConditionLock alloc] initWithCondition:2];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
// 1 和 创建的 conditionLock 条件值对比 - 1!=2,不执行
[conditionLock lockWhenCondition:1];
NSLog(@"线程 1");
[conditionLock unlockWithCondition:0];
});
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{
// 2 和 创建的 conditionLock 条件值对比 - 2=2,执行
[conditionLock lockWhenCondition:2];
NSLog(@"线程 2");
[conditionLock unlockWithCondition:1];
// 这里 condition 条件值设为了 1,会触发通知到在等待的线程1可以执行
});
dispatch_async(dispatch_get_global_queue(0, 0), ^{
[conditionLock lock];
NSLog(@"线程 3");
[conditionLock unlock];
});
// 执行结果:先线程2后线程1,3和2执行顺序不定
}
* Tip
: 原子锁 - atomic
// 原子锁模拟代码
- (NSString *)name {
return _name;
}
- (void)setName:(NSString *)name {
@synchronized (self) {
// 加锁,保证同一时间只有一条线程可进行写入
_name = name;
}
}
-
atomic
原子锁特点
1.默认的属性;
2.只在属性的setter
方法中添加了自旋锁 spin
,保证在同一时间,只有一条线程可进行写操作 --> 单写多读. setter 分析
3.一定程度保证科线程安全,但耗费大量资源。 -
nonatomic
1.非原子属性,没有锁,非线程安全;
2.性能高于atomic
;
建议
:
1. iOS开发中多用nonatomic
。因为atomic
耗费性能有点高,大概是nonatomic
的至少10倍
,客户端压力太大。
2. 尽量避免多线程抢夺同一块资源。若要抢夺资源并保证线程安全,可在相应位置单独加锁。