swift的多线程学习
同OC一样,分三者来学习,Thread,Operation和GCD
首先:Thread 每个对象对应一个线程,优点是量级较轻,使用简单,缺点是需要开发者自行管理线程的生命周期,线程同步,加锁解锁,睡眠以及唤醒灯操作
其次:Operation技术上不需要关心线程的管理和线程同步的事情,只需要关系业务的处理逻辑,缺点是只能实现它或者使用它的定义好的子类
最后:就是实际应用最多的GCD了,实际上它是底层的API。很强大,不仅仅决定代码块在哪一个线程中被执行,而且还可以根据可用的系统资源对这些线程进行处理
接下来就说一些简单的例子来说明:
Thread
开辟子线程下载图片,然后回到主线程进行刷新
imageView = UIImageView(frame:CGRect(x:0,y:260,width:320,height:300))
self.view .addSubview(imageView)
label = UILabel(frame:CGRect(x:0,y:0,width:320,height:300))
label.backgroundColor = UIColor.white
label.textAlignment = .center
label.text = "Loading..."
label.font = UIFont.systemFont(ofSize: 40)
self.view.addSubview(label)
let imageURL = "http://images.apple.com/v/iphone/home/s/home/images/trade_in_iphone_large_2x.jpg"
let thread = Thread(target:self, selector:#selector(ViewController.downloadImage),object:imageURL)
thread.start()
@objc func downloadImage(path:String) {
let url = URL(string:path)
var data:Data!
do{
try data = Data(contentsOf:url!)
let image = UIImage(data:data)
self.perform(#selector(ViewController.showImage),on:Thread.main,with:image,waitUntilDone:true)
}catch{
print("xiazaishibai")
}
因为我们要下载图片,如果放置在主线程上,就会影响当前的主线程,显示速度很慢。我们就将这个下载的任务,单独去开辟一个字线程,让它在子线程中下载,下载结束以后,然后刷新一下主线程就显示出来了,也不会对主线程造成阻塞的现象。
如果遇到多个字线程的问题,我们就需要开辟多个线程,然后同时需要加锁,这样就不会出现资源被重复利用的情况,比如我们现实生活中的购票操作。同时几个窗口售票问题。
let salesManA = Thread(target:self,selector:#selector(ViewController.sellBook),object:nil)
salesManA.name = "售货员A"
salesManA.start();
let salesManB = Thread(target:self,selector:#selector(ViewController.sellBook),object:nil)
salesManB.name = "售货员B"
salesManB.start();
let salesManC = Thread(target:self,selector:#selector(ViewController.sellBook),object:nil)
salesManC.name = "售货员C"
salesManC.start();
@objc func sellBook(){
while true {
lock.lock() //如果在这里不做一个加锁的处理就会造成资源被重复利用的情况,这里加锁以后每次只能有一个字线程执行操作
if bookCounts>0 {
Thread.sleep(forTimeInterval: 0.01)
soldBookCounts+=1
bookCounts-=1
let ThreadName = Thread.current.name
print("当前销售员为:\(ThreadName),此时售卖掉书:\(soldBookCounts),还剩下\(bookCounts)本")
}else{
Thread.exit()
}
lock.unlock()
}
}
Operation
如果我们需要同时下载几张照片,我们采用Operation的话,首先我们一般采用的是Operation的子类BlockOperation,然后我们将这些字线程加入到OperationQueue,值得注意的是 queue.maxConcurrentOperationCount = 1;//并发操作数,这里只要设置为1的话每次只能执行一个。
imageView1 = UIImageView(frame:CGRect(x:0,y:0,width:320,height:280))
imageView2 = UIImageView(frame:CGRect(x:0,y:280,width:320,height:280))
self.view.addSubview(imageView1)
self.view.addSubview(imageView2)
let downloadA = getOperation(name: "下载线程A", imageUrl: "http://images.apple.com/v/watch/k/images/overview/watch_03_large.jpg", isTopOne: true)
let downloadB = getOperation(name: "下载线程B", imageUrl: "http://images.apple.com/v/watch/k/images/overview/watch_05_large.jpg", isTopOne: false)
let queue = OperationQueue()
queue.maxConcurrentOperationCount = 1;//并发操作数
queue.addOperation (downloadA)
queue.addOperation(downloadB)
for operation in queue.operations {
print("Operation名称:"+operation.name!)
}
func getOperation(name:String,imageUrl:String,isTopOne:Bool)->BlockOperation{
let download = BlockOperation(block:{
let url = URL(string:imageUrl)
var data:Data!
do{
Thread.sleep(forTimeInterval: 1.0)
try data = Data(contentsOf:url!)
let image = UIImage(data:data)
if isTopOne{
self.perform(#selector(ViewController.showTopImage), on: Thread.main, with: image, waitUntilDone: true)
}else{
self.perform(#selector(ViewController.showFootImage), on: Thread.main, with: image, waitUntilDone: false)
}
}catch{
print("下载图片失败")
}
})
download.name = name
return download
}
@objc func showTopImage(image:UIImage){
self.imageView1.image = image
}
@objc func showFootImage(image:UIImage) {
self.imageView2.image = image
}
GCD的使用
全局队列来做一个数据处理和UI刷新的操作
label.frame = CGRect(x:0,y:0,width:320,height:568)
label.text = "loading..."
label.font = UIFont(name:"Arial",size:24)
label.backgroundColor = UIColor.green;
label.numberOfLines = 0
label.lineBreakMode = NSLineBreakMode.byWordWrapping
self.view.addSubview(label)
let apiUrl = URL(string:"http://ip.taobao.com/service/getIpinfo.php?ip=27.156.152.57")
let globalQueue = DispatchQueue.global()//创建一个全局变量,它是并发队列
globalQueue.async { //在block里面执行需要的查询任务
let result = try?Data(contentsOf:apiUrl!)
if (result==nil){
DispatchQueue.main.async {//回到主线程刷新UI
self.label.text = "解析不出来"
}
}else
{
let message = String(data:result!,encoding:String.Encoding.utf8)
DispatchQueue.main.async {
self.label.text = message
}
}
}
DispatchGroup 调度组的使用,实际上就是一个线程组,我们可以分别在不同的线程中执行不同的任务,当任务执行结束以后我们可以做一个通知的处理
//首先在界面上放置一个标签
label.frame = CGRect(x:0,y:0,width:320,height:568)
label.text = "loading..."
label.font = UIFont(name:"Arial",size:24)
label.backgroundColor = UIColor.green;
label.numberOfLines = 0
label.lineBreakMode = NSLineBreakMode.byWordWrapping
self.view.addSubview(label)
let group = DispatchGroup()
let globalQueue = DispatchQueue.global()//创建一个全局队列
globalQueue.async(group: group, execute: {
print("执行任务一")
})
globalQueue.async(group: group, execute: {
print("执行任务二")
})
globalQueue.async(group: group, execute: {
print("执行任务三")
})
group.notify(queue: globalQueue, execute: {
//检测到所有的任务都执行完了,我们可以做一个通知或者说UI的处理
print(" 任务都执行结束")
DispatchQueue.main.async {
self.label.removeFromSuperview()
}
})