GCD多线程基本使用

Grand Central Dispatch简称GCD,苹果官方推荐给开发者使用的首选多线程解决方案。

 实际应用

因为Alamofire已经实现了异步请求的功能,所以没法实践,这个界面当时我在设计的时候(T.T因为做的不好看被一起开发的室友吐槽然后改掉了一部分)是直接将数据存储在沙盒目录下的test.plist文件里面,所以可以将他来变为异步请求。

以下是具体实现:

当model数据为空时,调用initData方法异步请求文件里的数据,不影响UI的刷新

    override func viewDidLoad() {
        super.viewDidLoad()
        if(bodydata.count==0){
            initData()
        }
        configUI()
        configNavbar()
    }

 initData的实现,首先定义了一个并行队列,队列添加异步任务请求数据,请求结束之后切换回主线程刷新UI(因为UI刷新任务都必须放在主线程,不然会报错)。

    func initData(){
        let Queue = DispatchQueue(label: "task",attributes: .concurrent)
        Queue.async {
            if(plistdata?.count == nil)
            {
                let array:NSMutableArray = NSMutableArray()
                for item in origindata{
                    let dictionary:NSMutableDictionary = [ : ]
                    let center = item.centerText as NSString
                    dictionary.setValue(center, forKey: "centerText")
                    array.add(dictionary)
                }
                array.write(toFile: filePath, atomically: false)
                bodydata = origindata
            }
            else
            {
                for item in plistdata!{
                            let data = item as! NSDictionary
                            let center = data.value(forKey: "centerText") as! String
                            let sss = EditModel(centerText: center)
                            if(bodydata.count == 5)
                            {
                                break
                            }
                            else
                            {
                                bodydata.append(sss)
                            }
                        }
            }//切换到主线程刷新UI
            DispatchQueue.main.async {
                self.tableview.reloadData()
            }
        }
    }

基础认识

首先是队列的调度方式:并行和串行

并行:在本文中指并行队列,多个任务放在并行队列里执行,可以同时运行

串行:在本文中指串行队列,多个任务放在串行队列里执行,只能按顺序依次运行,前一个运行完成,下一个才能开始运行;前一个没运行完,后一个只能排队等着。以此类推,直到所有任务都运行完成。

                                                   并行

               

                                                   并发

任务的执行方式:同步和异步

同步:同步执行任务,在一个线程当中必须按顺序执行,执行结束的顺序也是固定的,而且同步任务会阻塞当前线程,直到同步任务结束才会结束阻塞,同步任务会经常造成线程的循环等待而导致死锁,慎用。

异步:异步执行任务,也是按顺序执行多项任务,但是是放在多个线程里同时运行,线程当中各个任务的开始执行时间和任务结束时间不固定,总耗时大约是耗时最长的那项任务所消耗的时间。

调度队列的种类:主队列,全局队列,自定义串行队列,自定义并行队列。

主队列(Main queue,串行队列):UI刷新的相关操作必须放在主队列

let mainQueue = DispatchQueue.main

全局队列(Global queue,并行队列):运行在后台线程,是系统内共享的全局队列,是一种并行队列(Concurrent),用于处理并发任务。因为是系统内共享,不单单属于这一个app,所以栅栏函数在全局队列中无效,为了避免堵塞其他程序的任务。

    1,全局队列没有名字,但是并发队列有名字。有名字可以便于查看系统日志

    2,全局队列是所有应用程序共享的。

    3,在mrc的时候,全局队列不用手动释放,但是并发队列需要。

let globalQueue = DispatchQueue.global()

自定义串行/并行队列(Custom queue):同样运行在后台。

//串行队列,label后为队列名字
let serialQueue = DispatchQueue(label: "fmy")

//并行队列
let concurrentQueue = DispatchQueue(label: "fmy", attributes: .concurrent)

线程调度的优先级(Qos)

  • userInteractive: 与用户交互相关的任务,要最重视,优先处理,保证界面最流畅
  • userInitiated: 用户主动发起的任务,要比较重视
  • default: 默认任务,正常处理即可
  • utility: 用户没有主动关注的任务
  • background: 不太重要的维护、清理等任务,有空能处理完就行
  • unspecified: 别说身份了,连身份证都没有,能处理就处理,不能处理也无所谓的

优先级低的任务要等优先级高的执行完,即使是并行队列。

基础使用

//定义2个调度任务,打印当前线程数据
let item1 = DispatchWorkItem {
    for i in 0...4{
        print("item1 -> \(i)  thread: \(Thread.current)")
    }
}

let item2 = DispatchWorkItem {
    for i in 0...4{
        print("item2 -> \(i)  thread: \(Thread.current)")
    }
}
//主队列追加异步任务,按顺序打印
let mainQueue = DispatchQueue.main
mainQueue.async(execute: item1)
mainQueue.async(execute: item2)


//全局队列追加异步任务,随机打印
let globalQueue = DispatchQueue.global()
globalQueue.async(execute: item1)
globalQueue.async(execute: item2)


//自定义串行队列追加异步任务,按顺序打印
let serialQueue = DispatchQueue(label: "serial")
serialQueue.async(execute: item1)
serialQueue.async(execute: item2)


//自定义并行队列追加异步任务,随机打印
let concurrentQueue = DispatchQueue(label: "concurrent", attributes: .concurrent)
concurrentQueue.async(execute: item1)
concurrentQueue.async(execute: item2)

//主队列追加同步任务,会引起死锁
let mainQueue = DispatchQueue.main
mainQueue.sync(execute: item1)
mainQueue.sync(execute: item2)

//全局队列追加同步任务,按顺序打印
let globalQueue = DispatchQueue.global()
globalQueue.sync(execute: item1)
globalQueue.sync(execute: item2)

//自定义串行队列追加同步任务,按顺序打印
let serialQueue = DispatchQueue(label: "serial")
serialQueue.sync(execute: item1)
serialQueue.sync(execute: item2)

//自定义并行队列追加同步任务,按顺序打印
let concurrentQueue = DispatchQueue(label: "concurrent", attributes: .concurrent)
concurrentQueue.sync(execute: item1)
concurrentQueue.sync(execute: item2)

死锁情况

1.主队列加入同步任务

2.串行队列当中的同步任务或者异步任务当中又嵌套了一个同步任务

原因分析:假设正在执行的任务是A任务(不管同步异步),A任务当中加入一个同步任务B(B任务属于A任务的一部分),要想执行完A任务,必须先执行B任务(因为同步任务阻塞线程),而调度队列又属于串行队列,A排在B前边,所以必须等任务A执行完才能执行任务B,这样就造成了循环等待,形成死锁。

//主队列添加同步任务
let mainQueue = DispatchQueue.main
mainQueue.sync(execute: item1)//同步任务
//自定义的串行队列
let serialQueue = DispatchQueue(label: "serial")
//死锁
serialQueue.sync {
    print("同步执行  thread: \(Thread.current)")
    serialQueue.sync {
        print("同步执行  thread: \(Thread.current)")
    }
}
//死锁
serialQueue.async {
    print("异步执行  thread: \(Thread.current)")
    serialQueue.sync {
        print("同步执行  thread: \(Thread.current)")
    }
}

3.不过自定义串行队列只加入同步任务不会导致死锁。煮个栗子:假如现在主线程在执行任务A(A任务在主队列当中),而串行队列加入了一个同步任务B,不管是什么队列加入同步任务,这个任务都会放到主线程去执行(分析见下文),所以这个时候,要想执行任务A,必须先执行任务B,而这个时候任务B可不可以执行呢?答案是可以,因为任务B在自定义的串行队列里面,而任务A在其他队列里面,这个时候任务B并没有被A给阻塞啊,所以是可以运行的,不会死锁。(所以说死锁针对的是队列,不是线程)

其他队列的同步任务也会放到主线程执行:

print("=> 开始执行")

let mainQueue = DispatchQueue.main
mainQueue.async(execute: item1)//异步任务

print("=> 执行完毕1")

let globalQueue = DispatchQueue.global()
globalQueue.sync(execute: item2)//同步任务

运行结果:
=> 开始执行
=> 执行完毕1
item2 -> 0  thread: <NSThread: 0x7fbf2cc0e7e0>{number = 1, name = main}
item2 -> 1  thread: <NSThread: 0x7fbf2cc0e7e0>{number = 1, name = main}
item2 -> 2  thread: <NSThread: 0x7fbf2cc0e7e0>{number = 1, name = main}
item2 -> 3  thread: <NSThread: 0x7fbf2cc0e7e0>{number = 1, name = main}
item2 -> 4  thread: <NSThread: 0x7fbf2cc0e7e0>{number = 1, name = main}
=> 执行完毕2
item3 -> 0  thread: <NSThread: 0x7fbf2cc0e7e0>{number = 1, name = main}
item3 -> 1  thread: <NSThread: 0x7fbf2cc0e7e0>{number = 1, name = main}
item3 -> 2  thread: <NSThread: 0x7fbf2cc0e7e0>{number = 1, name = main}
item3 -> 3  thread: <NSThread: 0x7fbf2cc0e7e0>{number = 1, name = main}
item3 -> 4  thread: <NSThread: 0x7fbf2cc0e7e0>{number = 1, name = main}
//用户的串行队列加入同步任务也是在主线程执行
let serialQueue = DispatchQueue(label: "serial")
serialQueue.sync(execute: item2)//同步任务

结果:
item2 -> 0  thread: <NSThread: 0x7fbf2cc0e7e0>{number = 1, name = main}
item2 -> 1  thread: <NSThread: 0x7fbf2cc0e7e0>{number = 1, name = main}
item2 -> 2  thread: <NSThread: 0x7fbf2cc0e7e0>{number = 1, name = main}
item2 -> 3  thread: <NSThread: 0x7fbf2cc0e7e0>{number = 1, name = main}
item2 -> 4  thread: <NSThread: 0x7fbf2cc0e7e0>{number = 1, name = main}


参考文章:详解Swift多线程 | licc

一天精通iOS Swift多线程(GCD) - 掘金

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值