iOS 多线程之线程锁Swift-Demo示例总结
线程锁是什么
在前面的文章中总结过多线程,总结了多线程之后,线程锁也是必须要好好总结的东西,这篇文章构思的时候可能写的东西得许多,只能挤时间一点点的慢慢的总结了,知道了线程之后要了解线程锁就得先了解一下什么是“线程锁”。
“线程锁”一段代码在同一个时间内是只能被一个线程访问,为了避免在同一时间内有多个线程访问同一段代码就有了“锁”的概念,比如说,线程A在访问着一段代码,进入这段代码之后我们加了一个“锁”。这个时候线程B又来访问了,由于有了锁线程B就会等待线程A访问结束之后解开了“锁”线程B就可以接着访问这段代码了,这样就避免了在同一时间内多个线程访问同一段代码!
相信上面的解释应该理解了“锁”的概念,也知道它是为了什么产生的,我们再举一个例子,一个房子一个人(线程)进去之后就把门锁上了,另一个人(线程)来了之后就在等待前面的人(线程)出来,等前面的人出来之后把门打开,它才可以进入房间。这样的解释相信应该明白了“锁”的概念,但是我们还是得强调一点,就是在这个“锁”和“解锁”之间不要有太多的“事”(执行代码,也就是任务)做,不然会造成过长时间的等待!就是去了多线程真正的含义和所用!
下面我们一个个的来解释我们常用的线程锁。
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`() -> Bool open func lock(before limit: Date) -> Bool @available(iOS 2.0, *)
open var name: String?
}
我们一个一个说说上面的方法:
1、lock和unlock 就是这个类最常用的两个方法,“加锁”和“解锁”的方法。
2、try()方法是尝试加锁,失败是不会阻塞线程的,如果获取锁失败就不会执行加锁代码。
3、lock(before limit: Date) 这个方法是在后面参数传入的时间内没有获取到线程锁就阻塞线程,要是到期还没有获取到线程锁就唤醒线程,这时候返回值是NO。
下面是我们Demo中具体的使用的例子代码:
var imageMutableArray:Array<Any> = Array.init()
let lock = NSLock.init() override func viewDidLoad() {
super.viewDidLoad() // Do any additional setup after loading the view.
for i in 0...1000 { imageMutableArray.append("imageArray==="+String(i))
}
print("你初始化的数组个数是",imageMutableArray.count )
} // MARK: - startTestBtnAction
override func removeFromDataImageArray() -> Void { // 我们使用多个线程去删除一个数组里面的东西,这样就有一个资源竞争的问题,我们看看
// 你可以先把这里的lock加锁个解锁的方法注释掉,代码会崩溃在imageMutableArray.removeFirst()
// 关于这样写(不加锁)时候的线程安全的问题 http://www.jianshu.com/p/2fce6a0bb17b while (true) { lock.lock()
if (imageMutableArray.count > 0) { imageMutableArray.removeFirst()
}else{ now = CFAbsoluteTimeGetCurrent()
let resultString = "操作开始时间:" + String(describing: then) + "\n结束时间:"+String(describing: now) + "\n整个操作用时:"+String(now! - then!) + "ms"
/*
NOTE: 修改UI要在主线程,不能在子线程,刚开始疏忽报了下面的错误
*/
DispatchQueue.main.async {
self.resulttextView.text = resultString
}
return
} lock.unlock()
}
}
NSCondition
NSCondition条件锁,首先它也是遵循NSLocking协议的,这点和我们上面说的NSLock是一致的,所以它的加锁和解锁方式和我们前面说的NSLock是一样的,就是lock和unlock方法,你要是简单的使用它来解决线程同步的问题,那他简单的用法和前面写的NSLock也是一样的。但我们要是把NSCondition当NSLock用那就真的是浪费了!NSCondition还有自己的wait和signal用法,这个和后面信号量有点点类似,信号量的我们下面再说,看看NSCondition部分的代码:
// MARK: - startTestBtnAction
override func removeFromDataImageArray() -> Void { while (true) { lock.lock()
if (imageMutableArray.count > 0) { imageMutableArray.removeFirst()
}else{ now = CFAbsoluteTimeGetCurrent()
let resultString = "操作开始时间:" + String(describing: then) + "\n结束时间:"+String(describing: now) + "\n整个操作用时:"+String(now! - then!) + "ms" DispatchQueue.main.async { self.resulttextView.text = resultString
}
return
}
lock.unlock()
}
}
NSConditionLock
NSConditionLock同样实现了NSLocking协议,不过测试一下之后你会发现这个性能比较低。NSConditionLock也能像NSCondition一样能进行线程之间的等待调用,并且还是线程安全的。下面使我们Demo中的代码,写给这里的只是最基本的加锁解锁的代码,先看看:
var imageMutableArray:Array<Any> = Array.init()
let lock = NSConditionLock.init() override func viewDidLoad() {
super.viewDidLoad() // Do any additional setup after loading the view.
for i in 0...1000 { imageMutableArray.append("imageArray==="+String(i))
}
print("NSCondition初始化的数组个数是",imageMutableArray.count ) } // MARK: - startTestBtnAction
override func removeFromDataImageArray() -> Void { while (true) { lock.lock()
if (imageMutableArray.count > 0) { imageMutableArray.removeFirst()
}else{ now = CFAbsoluteTimeGetCurrent()
let resultString = "操作开始时间:" + String(describing: then) + "\n结束时间:"+String(describing: now) + "\n整个操作用时:"+String(now! - then!) + "ms" DispatchQueue.main.async { self.resulttextView.text = resultString
}
return
}
lock.unlock()
}
}
NSRecursiveLock
有时候“加锁代码”中存在递归调用,递归开始前加锁,递归调用开始后会重复执行此方法以至于反复执行加锁代码最终造成死锁,这个时候可以使用递归锁来解决,也就是我们的NSRecursiveLock,它就是递归锁!使用递归锁可以在一个线程中反复获取锁而不造成死锁,在这个过程中也会记录获取锁和释放锁的次数,只有等两者平衡的时候才会释放,下面是我们Demo中的示例:
// 递归调用
func removeFromImageArray() -> Void { recursiveLock.lock()
if (imageMutableArray.count>0) { imageMutableArray.removeFirst()
self.removeFromImageArray()
}
recursiveLock.unlock()
} // MARK: - removeFromDataImageArray
// 模仿递归调用
override func removeFromDataImageArray() -> Void { let dispatchGroup = DispatchGroup.init()
let dispatchQueue = DispatchQueue.init(label:queueLabel, qos: .default, attributes: .concurrent)
dispatchQueue.async(group: dispatchGroup, qos: .default, flags: DispatchWorkItemFlags.noQoS) { self.removeFromImageArray()
} dispatchGroup.notify(queue: DispatchQueue.main) { self.now = CFAbsoluteTimeGetCurrent()
let resultString = "操作开始时间:" + String(describing: self.then) + "\n结束时间:"+String(describing: self.now) + "\n整个操作用时:"+String(self.now! - self.then!) + "ms"
self.resulttextView.text = resultString
}
}
@synchronized
@synchronized你要说它简单,它的用法的确都是比较简单的,要想深了探究一下它的话,它里面的东西还的确是挺多的!但我们是在Swift中来讨论线程锁的,这里也就不能再使用 @synchronized,因为在Swift中它是不在使用了的,相应代替它的是下面下面这两句:objc_sync_enter() 中间是你需要加锁的代码 objc_sync_exit() ,那上面相同的操作我们用这个互斥锁写的话代码如下:
// MARK: - removeFromDataImageArray
override func removeFromDataImageArray() -> Void { while (true) {
//互斥锁
objc_sync_enter(self)
if (imageMutableArray.count > 0) { imageMutableArray.removeFirst()
}else{ now = CFAbsoluteTimeGetCurrent()
let resultString = "操作开始时间:" + String(describing: then) + "\n结束时间:"+String(describing: now) + "\n整个操作用时:"+String(now! - then!) + "ms"
DispatchQueue.main.async { self.resulttextView.text = resultString
}
return
}
objc_sync_exit(self)
}
}
dispatch_semaphore_t 信号量
dispatch_semaphore_t是属于GCD里面的东西,在前面总结多线程的时候我们说把它放在我们总结线程锁的时候说,在这里我们就说一些这个信号量,dispatch_semaphore_t 和前面@synchronized一样都是我们OC的写法,在我们的Swift中也不是这样写的,全部的内容都是在DispatchSemaphore中,关于GCD方面API的对比我们在下面做了一张表,大致的说一下:
你看完了这张图的对比以及总结之后,我们说回我们的主题:DispatchSemaphore 可以看到它的主要的方法:
open class DispatchSemaphore : DispatchObject {
} /// dispatch_semaphore
extension DispatchSemaphore {
// 发送信号,让信号量+1方法
public func signal() -> Int
// 等待,让信号量-1方法
public func wait()
// 下面两个方法可以设置等待的时间,过了这个时间要是没有让信号量大于或者等于初始化的信号量值的时候
// 就会自己接着往执行代码,相等于我们的锁是失效了的
public func wait(timeout: DispatchTime) -> DispatchTimeoutResult public func wait(wallTimeout: DispatchWallTime) -> DispatchTimeoutResult
}
extension DispatchSemaphore { /*!
* @function dispatch_semaphore_create
*
* @abstract
* Creates new counting semaphore with an initial value.
*
* @discussion
* Passing zero for the value is useful for when two threads need to reconcile
* the completion of a particular event. Passing a value greater than zero is
* useful for managing a finite pool of resources, where the pool size is equal
* to the value.
*
* @param value
* The starting value for the semaphore. Passing a value less than zero will
* cause NULL to be returned.
*
* @result
* The newly created semaphore, or NULL on failure.
*/
@available(iOS 4.0, *)
public /*not inherited*/ init(value: Int)
}
OC和Swift的用法是一样的,只是在写法上有一些的区别,这里就不再说OC的了,我们直接看看Swift的代码怎么写:
// MARK: - startTestBtnAction
override func removeFromDataImageArray() -> Void { while (true) { /* 也可以直接写: semaPhore.wait()
这里发生一个等待信号,信号量就-1,变成0 ,后面的任务就会处于等待状态,
等到信号量大于等于1的时候在执行,要是信号量不大于或者等于你初始化时候的值,它就一直处于等待状态
当然,你也可以在这里这是等待的时间 semaPhore.wait(timeout: DispatchTime.init(uptimeNanoseconds: UInt64(10)))
但过了这个时间之后在进入就等于是我们的锁失效了。面临的问题也就是相应的崩溃,在删除方法哪里,可以自己试一下
*/
_ = semaPhore.wait(timeout: DispatchTime.distantFuture) if (imageMutableArray.count > 0) { imageMutableArray.removeFirst()
}else{ now = CFAbsoluteTimeGetCurrent()
let resultString = "操作开始时间:" + String(describing: then) + "\n结束时间:"+String(describing: now) + "\n整个操作用时:"+String(now! - then!) + "ms" DispatchQueue.main.async { self.resulttextView.text = resultString
} // 不要忘记在这里加处理,不然return之后是执行不到下面的semaPhore.signal()代码
semaPhore.signal()
return
} // signal() 方法,这里会使信号量+1
semaPhore.signal()
}
}
POSIX
POSIX和我们前面写的dispatch_semaphore_t用法是挺像的,但探究本质的haul它们根本就不是一个东西,POSIX是Unix/Linux平台上提供的一套条件互斥锁的API。你要是在OC的文件中只用的话你需要导入头文件:pthread.h
在Swift中就不用了,但是在使用的时候不管是OC的还是Swift的,代码是一致的,它的几个主要的方法就是下面三个,剩下的具体的代码可以看demo或者是下面基本的方法:
1、pthread_mutex_init 初始化方法
2、pthread_mutex_lock 加锁方法
3、pthread_mutex_unlock 解锁方法
class POSIXController: ThreadLockController { var imageMutableArray:Array<Any> = Array.init() var mutex:pthread_mutex_t = pthread_mutex_t() //初始化pthread_mutex_t类型变量 override func viewDidLoad() {
super.viewDidLoad() // 初始化
pthread_mutex_init(&mutex,nil)
// Do any additional setup after loading the view.
for i in 0...1000 { imageMutableArray.append("imageArray==="+String(i))
}
print("NSLock初始化的数组个数是",imageMutableArray.count)
}
// MARK: - startTestBtnAction
override func removeFromDataImageArray() -> Void { while (true) { // 加锁
pthread_mutex_lock(&mutex)
if (imageMutableArray.count > 0) { imageMutableArray.removeFirst()
}else{ now = CFAbsoluteTimeGetCurrent()
let resultString = "操作开始时间:" + String(describing: then) + "\n结束时间:"+String(describing: now) + "\n整个操作用时:"+String(now! - then!) + "ms" DispatchQueue.main.async { self.resulttextView.text = resultString
}
pthread_mutex_unlock(&mutex);
return
}
// 解锁
pthread_mutex_unlock(&mutex)
}
}
/*
Swift 的deinit函数实际的作用和OC中的dealloc函数是一样的
对象的释放 通知 代理等等的处理都是在这里处理的
*/
deinit {
pthread_mutex_destroy(&mutex); //释放该锁的数据结构
}
}
剩下的还有什么
1、OSSpinLock
首先要提的是OSSpinLock已经出现了BUG,导致并不能完全保证是线程安全,所以这个我们知道,大概的了解一下,具体的问题可以去这里仔细看看:不再安全的 OSSpinLock
2、dispatch_barrier_async/dispatch_barrier_sync
这个我在前面总结GCD的时候说过了这个“栅栏”函数,就不在这里重复说了
3、最后就是Demo的地址了,这个Demo原本是想用Swift试着模仿一下微信的UI的,包括聊天框架那部分,以前写过OC的,这次春被用Swift写一下,主要也是为了用一下Swift,以及看一下4.0它的一些新的特性,不然很久不写,一些东西比较容易遗忘!
iOS 多线程之线程锁Swift-Demo示例总结的更多相关文章
- IOS 多线程,线程同步的三种方式
本文主要是讲述 IOS 多线程,线程同步的三种方式,更多IOS技术知识,请登陆疯狂软件教育官网. 一般情况下我们使用线程,在多个线程共同访问同一块资源.为保护线程资源的安全和线程访问的正确性. 在IO ...
- 【原】iOS多线程之线程间通信和线程互斥
线程间通信 1> 线程间通信分为两种 主线程进入子线程(前面的方法都可以) 子线程回到主线程 2> 返回主线程 3> 代码 这个案例的思路是:当我触摸屏幕时,会在子线程加载图片,然后 ...
- 关于Unity中协程、多线程、线程锁、www网络类的使用
协程 我们要下载一张图片,加载一个资源,这个时候一定不是一下子就加载好的,或者说我们不一定要等它下载好了才进行其他操作,如果那样的话我就就卡在了下载图片那个地方,傻住了.我们希望我们只要一启动加载的命 ...
- python 多线程、线程锁、事件
1. 多线程的基本使用 import threading import time def run(num): print('Num: %s'% num) time.sleep(3) if num == ...
- IOS多线程之线程的创建
版权声明:原创作品,谢绝转载!否则将追究法律责任. 之前也说过线程是消耗资源的.多线程会占用你应用程序(和系统的)的内存使用和性能方面的资源.我们创建一个线程后可以对他的一些部分进行配置例如可以对 ...
- 【死磕线程】线程同步机制_java多线程之线程锁
1.线程各种状态间的切换,用图表示的话简单清晰: 图出处:https://www.cnblogs.com/bhlsheji/p/5099362.html(博主对每个状态解释的清晰明了) 2.为什么需要 ...
- python多线程、线程锁
1.python多线程 多线程可以把空闲时间利用起来 比如有两个进程函数 func1.func2,func1函数里使用sleep休眠一定时间,如果使用单线程调用这两个函数,那么会顺序执行这两个函数 也 ...
- IOS多线程之线程属性的配置
版权声明:原创作品,谢绝转载!否则将追究法律责任. 设置线程堆栈的大小: 系统为每个你新创建的线程,都会为你的进程空间分配一定的内存作为该线程的堆栈.这里面有我们局部变量声明我们的方法就是一个堆栈 ...
- C#多线程,线程锁
]; ; i < ; i++) { threads[i]= ; i < ; i++) { R ...
随机推荐
- 跨站请求伪造(CSRF)-简述
跨站请求伪造(CSRF)-简述 跨站请求伪造(英语:Cross-site request forgery),也被称为 one-click attack 或者 session riding,通常缩写为 ...
- JavaScript面向对象中的继承
1.1继承的基本概念 使用一个子类,继承另一个父类,那么子类可以自动拥有父类中的所有属性和方法,这个过程叫做继承. >>>继承的两方,发生在两个类之间. 实现继承的三种方式: 扩展O ...
- Mybatis Generator生成Mybatis Dao接口层*Mapper.xml以及对应实体类
[前言] 使用Mybatis-Generator自动生成Dao.Model.Mapping相关文件,Mybatis-Generator的作用就是充当了一个代码生成器的角色,使用代码生成器不仅可以简化我 ...
- 分布式数据库TiDB的部署
转自:https://my.oschina.net/Kenyon/blog/908370 一.环境 CentOS Linux release 7.3.1611 (Core)172.26.11.91 ...
- 学习UML --用例图
用例图用于描述系统提供的系列功能.使用用例图的主要目的是帮助开发团队以一种可视化的方式理解系统的功能需求.用例图对系统的实现不做任何说明,仅仅是系统功能的描述. 用例图主要在需求分析阶段使用,用于描述 ...
- 删除链表中等于给定值val的所有节点。
样例 给出链表 1->2->3->3->4->5->3, 和 val = 3, 你需要返回删除3之后的链表:1->2->4->5. /** * D ...
- Stars(二维树状数组)
Stars Time Limit: 5000/2000 MS (Java/Others) Memory Limit: 32768/65536 K (Java/Others) Total Submiss ...
- Fibonacci Check-up
Fibonacci Check-up Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) ...
- IdentityServer4 登录成功后,跳转到原来页面
IdentityServer4 登录成功后,默认会跳转到Config.Client配置的RedirectUris地址http://localhost:5003/callback.html,用于获取 T ...
- 解决Android下元素滑动问题
移动端左右.上下滑动: 当页面中既需要页面滑动操作,又需要上下或左右滑动页面上的某个元素时,直接使用zepto中提供的swipe事件是不能直接达到目的的,原因如下: (1)在Android低端机上to ...