Foundation还提供了NSLock,通过显示定义同步锁对象来实现同步,在这种机制下,同步锁使用NSLock对象充当。 NSLock是控制多个线程对共享资源进行访问的工具。 通常锁提供了对共享资源的独占访问,每次只能有一个线程对NSLock对象加锁,线程开始访问共享资源之前应县获得NSLock对象。 在实现线程安全的控制中,使用该NSLock对象可以显式的加锁、释放锁。 下面我们来举个简单的 NSLock* lock; - (id)init { self = [super init]; if (self) { lock = [[NSLock 对此Foundation提供了NSCondition类来处理多线程之间的通信,NSCondition实现了NSLock协议,因此也可以调用lock、unlock来实现线程同步。
上面的情形,我们直接使用NSLock来处理: ? 此时就没什么问题了。 NSLock 上面?我们已经演示了NSLock的使用场景,接下来我们看一下NSLock的实现源码。 的源码,然后找到NSLock的实现: open class NSLock: NSObject, NSLocking { internal var mutex = _MutexPointer.allocate } 可以看到,NSLock实际上就是对系统底层互斥锁的简单封装。 接下来看个例子: NSLock *lock = [[NSLock alloc] init]; dispatch_async(dispatch_get_global_queue(0, 0), ^{ 对于@synchronized、NSLock和NSRecursiveLock的分析,我们总结了一些小观点: 对于一些普通的线程安全操作,使用NSLock就差不多了 在需要递归调用的方法或者函数中使用的锁
类 NSLock对象为Cocoa应用程序实现了一个基本的互斥锁。 所有锁(包括NSLock)的接口实际上是由NSLock协议定义的,它定义了锁和解锁方法。我们可以使用这些方法来获取和释放锁,就像使用任何互斥锁一样。 除了标准的锁定行为,NSLock类还添加了 tryLock 和 lockBeforeDate: 方法。 tryLock 方法尝试获取锁,但在锁不可用时不会阻塞;相反,该方法只是返回NO。 以下示例演示如何使用NSLock对象来协调可视化显示器的更新,该显示器的数据由多个线程计算。如果线程无法立即获取锁,它只需继续计算,直到它能够获取锁并更新显示器。 BOOL moreToDo = YES; NSLock *theLock = [[NSLock alloc] init]; ... while (moreToDo) { /* 做另一个增量的计算
性能分析 通过一个简单的demo来分析iOS中常用的一些锁的性能(@synchronized,NSLock,pthread,OSSpinLock,dispatch_semaphore_t,pthread_mutex_t [lock unlock]; } end = CACurrentMediaTime(); timeCosts = end - begin; // NSLock NSLock *lock = [NSLock new]; begin = CACurrentMediaTime(); for (int i = 0; i 1000000次加锁解锁的基础进行测试(模拟器中),结果数据如下: OSSpinLock os_unfair_lock dispatch_semaphore pthread_mutex NSCondition NSLock
NSLock NSLock是最简单的互斥锁,下面的NSCondition、NSConditionLock以及NSRecursiveLock都是遵守了NSLocking协议的,我们就放在一起说 ,包括我们现在说的NSLock也是,我们看看这个NSLock里面具体都有什么,先看看它代码里面的方法: public protocol NSLocking { public func lock() public func unlock() } open class NSLock : NSObject, NSLocking { open func `try 是一致的,所以它的加锁和解锁方式和我们前面说的NSLock是一样的,就是lock和unlock方法,你要是简单的使用它来解决线程同步的问题,那他简单的用法和前面写的NSLock也是一样的。 但我们要是把NSCondition当NSLock用那就真的是浪费了!
如何解决 在多线程操作过程中,如何保护共享数据,其实已经是一个众所周知的事情了,这里总结下自己试过的处理方法: @synchronized NSLock dispatch_semaphore_signal dispatch_semaphore_t semaphore; } @property (assign, nonatomic) NSInteger ticketNumber; @property (strong, nonatomic) NSLock self.ticketNumber = 100; self.lock = [[NSLock alloc]init]; semaphore = dispatch_semaphore_create }else{ // 退出当前线程 [NSThread exit]; } } } } 使用NSLock
解锁 pthread_mutex_unlock(&mutex_t); // 尝试加锁,可以加锁时返回的是 0,否则返回一个错误 pthread_mutex_trylock(& mutex_t) 5、NSLock (互斥锁、对象锁) // 初始化 NSLock *_lock = [[NSLock alloc]init]; // 加锁 [_lock lock]; // 解锁 [_lock unlock]; // 尝试加锁 其他功能接口 wait 进入等待状态 waitUntilDate:让一个线程等待一定的时间 signal 唤醒一个等待的线程 broadcast 唤醒所有等待的线程 注: 所测时间波动太大, 有时候会快于 NSLock NSLock 封装的pthread_mutex的PTHREAD_MUTEX_NORMAL 模式 NSRecursiveLock 封装的pthread_mutex的PTHREAD_MUTEX_RECURSIVE
CFAbsoluteTimeGetCurrent(); NSLog(@"@synchronized: %f ms", (current - lastTime) * 1000); 2、NSLock 、NSLock+IMP // NSLock NSLock *myLock = [NSLock new]; lastTime = CFAbsoluteTimeGetCurrent [myLock unlock]; } current = CFAbsoluteTimeGetCurrent(); NSLog(@"NSLock : %f ms", (current - lastTime) * 1000); // NSLock + IMP typedef void (*func) : 37.993014 ms 2015-05-25 16:46:26.775264+0800 Tst[69225:10974999] NSLock + IMP: 37.218988 ms 2015-05
(synonymous) 要实现一个原子属性,可以通过锁来实现,在Swift中通过不同的Apple框架的锁都可以实现这点: 通过Property Wrappers来定义一个原子的属性装饰器 在此使用NSLock @propertyWrapper struct Atomic<Value> { private var value: Value private let lock = NSLock()
NSStringFromSelector(_cmd)); } - (void)method2 { NSLog(@"%@",NSStringFromSelector(_cmd)); } @end 1.使用NSLock 实现的锁 //主线程中 TestObj *obj = [[TestObj alloc] init]; NSLock *lock = [[NSLock alloc] init]; //线程1 dispatch_async
2、NSLock NSLock * lock = [[NSLock alloc]init]; [lock lock]; //执行的代码操作 [lock unlock]; 底层通过pthread_mutex 3、NSRecursiveLock 递归锁类似NSLock,但它可以在同一个线程中反复加锁且不会造成死锁。 4、 NSCondition 基于信号量方式实现的锁对象,提供单独的信号量管理接口。 159195] 等待条件满足 [9581:159195] 条件满足了 [9581:159195] 执行操作 [9581:159195] 完成 总结 常用的线程间同步方式就这些了,我实际中用的信号量和NSLock
synchronization - (void)synchronizedMethod{ @synchronized(self) { // 关键代码; } } @NSLock _ lock = [[NSLock alloc]init]; - (void)synchronizedMethod{ [_ lock lock]; // 关键代码;
NSLock 对象锁 3. dispatch_semaphore 信号量 4.NSCondition 条件锁 5.NSConditionLock 6.NSRecursiveLock 是递归锁 一.synchronized NSLock NSLock 遵循 NSLocking 协议,lock 方法是加锁,unlock 是解锁,tryLock 是尝试加锁,如果失败的话返回 NO,lockBeforeDate: 是在指定Date () /** 票的总数 */ @property (nonatomic, assign) NSInteger ticketNumber; @property (strong, nonatomic) NSLock - (void)viewDidLoad { [super viewDidLoad]; self.ticketNumber = 100; self.lock = [[NSLock
4、NSLock NSLock是对mutex PTHREAD_MUTEX_DEFAULT 普通锁的封装 - (void)lock;// 加锁 - (void)unlock;//解锁 - (BOOL)tryLock 直接加锁返回YES - (BOOL)lockBeforeDate:(NSDate *)limit; //在一定时间内是否可以加锁 如果可以 直接加锁返回YES 复制代码 5、NSRecursiveLock NSLock 是对mutex PTHREAD_MUTEX_RECURSIVE递归锁的封装 API和NSLock一样 - (void)lock;// 加锁 - (void)unlock;//解锁 - (BOOL)tryLock
NSLock *lock = [[NSLock alloc] init]; dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT addObject:items[i]]; NSLog(@"%@", self.items); pthread_mutex_unlock(&_pthread); // 解锁 }); } 5、NSLock 使用例子: self.lock = [[NSLock alloc] init]; self.lock.name = @"itemsLock"; NSArray *items = @[@"1", @"2 NSLog(@"%@", self.items); [self.lock unlock]; // 解锁 }); } 6、NSCondition 条件锁 内部有实现NSLocking协议, 就能实现NSLock
讨论NSLock.Lock 加 Await 加 NSLock.Unlock 导致主线程冻结[5] 提问: 以下代码模拟了当外部库的作者引入锁时的情况,这可能包含等待调用。 let lock = NSLock() func thirdPartyLibLock() { print("- do sum work and lock") lock.lock() 并解释了为什么选择反斜杠: https://github.com/apple/swift-evolution/blob/main/proposals/0161-key-paths.md#spelling [5] NSLock.Lock 加 Await 加 NSLock.Unlock 导致主线程冻结: https://forums.swift.org/t/nslock-lock-plus-await-plus-nslock-unlock-leads-to-main-thread-freeze
9.1 NSOperation、NSOperationQueue 非线程安全 先来看看不考虑线程安全的代码: /** * 非线程安全:不使用 NSLock * 初始化火车票数量、卖票窗口(非线程安全 可以看到:在不考虑线程安全,不使用 NSLock 情况下,得到票数是错乱的,这样显然不符合我们的需求,所以我们需要考虑线程安全问题。 这里我们使用 NSLock 对象来解决线程同步问题。NSLock 对象可以通过进入锁时调用 lock 方法,解锁时调用 unlock 方法来保证线程安全。 考虑线程安全的代码: /** * 线程安全:使用 NSLock 加锁 * 初始化火车票数量、卖票窗口(线程安全)、并开始卖票 */ - (void)initTicketStatusSave { 可以看出:在考虑了线程安全,使用 NSLock 加锁、解锁机制的情况下,得到的票数是正确的,没有出现混乱的情况。我们也就解决了多个线程同步的问题。 10.
可以看到:在不考虑线程安全,不使用 NSLock 情况下,得到票数是错乱的,这样显然不符合我们的需求,所以我们需要考虑线程安全问题。 这里我们使用 NSLock 对象来解决线程同步问题。NSLock 对象可以通过进入锁时调用 lock 方法,解锁时调用 unlock 方法来保证线程安全。 考虑线程安全的代码: /** * 线程安全:使用 NSLock 加锁 * 初始化火车票数量、卖票窗口(线程安全)、并开始卖票 */ - (void)initTicketStatusSave { alloc] init]; // 初始化 NSLock 对象 // 1.创建 queue1,queue1 代表北京火车票售卖窗口 NSOperationQueue *queue1 可以看出:在考虑了线程安全,使用 NSLock 加锁、解锁机制的情况下,得到的票数是正确的,没有出现混乱的情况。我们也就解决了多个线程同步的问题。
之前我对,互斥量只由一个线程获取和释放,理解的比较狭义,以为这里的获取和释放,是系统强制要求的,用 NSLock 实验发现它可以在不同线程获取和释放,感觉很疑惑。 @synchronized (obj) { _count = _count - 1; [self testLock]; } } } 而如果换成NSLock 而我们在 iOS 中使用的 NSLock,NSRecursiveLock等都是基于pthread_mutex 做实现的。 NSLock NSLock属于 pthread_mutex的一层封装, 设置了属性为 PTHREAD_MUTEX_ERRORCHECK 。 它会损失一定性能换来错误提示。 对于平时编写应用里的多线程代码,还是建议用 @synchronized,NSLock 等,可读性和安全性都好,多线程安全比多线程性能更重要。
主要的方法有以下几种: 互斥锁 使用@synchronized解决线程同步问题相比较NSLock要简单一些,但是效率是众多锁中最差的。 leftTicketsCount); } else { //退出线程 [NSThread exit]; } } 同步锁NSLock iOS中对于资源抢占的问题可以使用同步锁NSLock来解决,使用时把需要加锁的代码(以后暂时称这段代码为”加锁代码“)放到NSLock的lock和unlock之间。