美文网首页多线程
Swift补充- 多线程(DispatchQueue & Dis

Swift补充- 多线程(DispatchQueue & Dis

作者: 看谷秀 | 来源:发表于2022-07-21 17:37 被阅读0次

    目录

    1. DispatchQueue
    2. DispatchWorkItem

    1 DispatchQueue 队列 async/ sync

    public convenience init(label: String,  // 标识符
    qos: DispatchQoS = .unspecified,  // 优先级
    attributes: DispatchQueue.Attributes = [], // 指定队列
    autoreleaseFrequency: DispatchQueue.AutoreleaseFrequency = .inherit, // 队列释放策略
    target: DispatchQueue? = nil ) // 目标队列  
    

    (参数-1) label:String : 队列的标识符,方便在调试工具(如 Instruments, 奔溃日志)中找到对应的信息。

    (参数-2) qos: DispatchQoS : 该值确定系统安排任务执行的优先级。
    QoS类对要在DispatchQueue上执行的工作进行了分类。 通过指定任务的qos,可以表明任务对应用程序的重要性。
    在安排任务时,系统会优先处理服务级别较高的任务。因为高优先级的工作比低优先级的工作执行得更快,资源更多,所以与低优先级的工作相比,通常需要更多的精力。 为您的应用执行的工作准确地指定适当的QoS类可确保您的应用具有响应能力和能源效率。

    优先级 background < utility < default < userInitiated < userInteractive

      1. public static let background: DispatchQoS 在所有任务中具有最低的优先级。针对当APP在后台运行的时候,需要处理的任务
      1. public static let utility: DispatchQoS 优先级低于default, userInitiated, userInteractive,高于background。 将类型分配给不会阻止用户继续使用您的应用程序的任务。 例如,您可以将此类分配给长时间运行的任务,而这些任务的进度用户并未积极关注。
      1. public static let default: DispatchQoS 优先级低于 userInitiated, userInteractive,但高于utility和background。将此类型分配给应用启动或代表用户执行活动的任务或队列。
      1. public static let userInitiated: DispatchQoS 优先级仅仅低于 userInteractive。 将此类型分配给可以为用户的操作提供即时结果的任务,或者将阻止用户使用您的应用的任务。 例如,您可以使用此类型加载要显示给用户的电子邮件的内容。
      1. public static let userInteractive: DispatchQoS 在所有任务中具有最高的优先级。将此类型分配给可与用户交互或主动更新应用程序的用户界面的任务或队列。 例如,将此用于动画类或跟踪事件。
      1. public static let unspecified: DispatchQoS 未设置优先级

    在OC中的定义

    typedef uint32_t dispatch_qos_t;
    typedef uint32_t dispatch_priority_t;
    
    #define DISPATCH_QOS_UNSPECIFIED        ((dispatch_qos_t)0)
    #define DISPATCH_QOS_MAINTENANCE        ((dispatch_qos_t)1)
    #define DISPATCH_QOS_BACKGROUND         ((dispatch_qos_t)2)
    #define DISPATCH_QOS_UTILITY            ((dispatch_qos_t)3)
    #define DISPATCH_QOS_DEFAULT            ((dispatch_qos_t)4)
    #define DISPATCH_QOS_USER_INITIATED     ((dispatch_qos_t)5)
    #define DISPATCH_QOS_USER_INTERACTIVE   ((dispatch_qos_t)6)
    #define DISPATCH_QOS_MIN                DISPATCH_QOS_MAINTENANCE
    #define DISPATCH_QOS_MAX                DISPATCH_QOS_USER_INTERACTIVE
    #define DISPATCH_QOS_SATURATED          ((dispatch_qos_t)15)
    

    (参数-3) attributes: DispatchQueue.Attributes 与队列关联的属性。 包括并发属性以创建可以同时执行任务的调度队列。 如果省略该属性,则分派队列将串行执行任务。

    • static let concurrent: DispatchQueue.Attributes 如果不存在此属性,则队列按先进先出(FIFO)顺序依次调度任务。也就是只要不设置这个属性那么创建的队列就是串行队列

    • static let initiallyInactive: DispatchQueue.Attributes 通常新创建的队列已提交的块会立即执行。 使用此属性可以防止队列调度块,直到调用其activate()方法为止。

    //激活在非活动状态下创建的队列
    @available(iOS 10.0, *)
    public func activate()
    
    //可以挂起一个队列,就是把这个线程暂停了,它占着资源,但不运行。
    @available(iOS 4.0, *)
    public func suspend()
    //可以继续挂起的队列,让这个线程继续执行下去。
    @available(iOS 4.0, *)
    public func resume()
    

    (参数-4) autoreleaseFrequency: DispatchQueue.AutoreleaseFrequency 调度队列自动释放对象的频率。

      1. inherit 创建的队列的默认行为。
      1. workItem 队列在执行块之前配置一个自动释放池,并在块执行完之后释放该池中的对象。
      1. never 队列未在执行的块周围设置自动释放池。此选项是系统定义的全局队列的默认行为。

    (参数-1) target: DispatchQueue? 要在其上执行块的目标队列。 如果希望系统提供适合于当前对象的队列,请指定DISPATCH_TARGET_QUEUE_DEFAULT。

    //在OC中的定义
    void
    dispatch_set_target_queue(dispatch_object_t object,
            dispatch_queue_t _Nullable queue);
    

    设置taget的作用 官方文档

    • 针对Dispatch queues

    • 将所有块从当前调度队列重定向到指定的目标队列。使用目标队列将工作从几个不同的队列重定向到单个队列。这样做以最大程度地减少应用程序使用的线程总数,同时仍保留所需的执行语义。如果一个dispatch queue有目标队列,系统是不会给这个队列分配线程的,除非他的目标队列是全局队列。

    • 我们可以通过下面的例子发现,如果队列设置了target,他是否分配线性是根据它的目标队列,在串行队列执行异步操作只会创建一个线程,所以当设置了target,打印的所有线程都是一样的。如果不设置target,并发队列执行异步操作是会创建多个线程,所以打印的线程都是不一样的。

    let queue1 = DispatchQueue.init(label: "串行队列1")
    let queue2 = DispatchQueue.init(label: "并发队列",attributes: .concurrent,target: queue1)
    for i in 0..<10 {
        queue2.async { 
            print("queue2 任务\(i)",Thread.current)
        }
    }
    /*
    // 有目标target队列 不分配新队列
    queue2 任务0 <NSThread: 0x600003449580>{number = 7, name = (null)}
    queue2 任务1 <NSThread: 0x600003449580>{number = 7, name = (null)}
    queue2 任务2 <NSThread: 0x600003449580>{number = 7, name = (null)}
    queue2 任务3 <NSThread: 0x600003449580>{number = 7, name = (null)}
    queue2 任务4 <NSThread: 0x600003449580>{number = 7, name = (null)}
    queue2 任务5 <NSThread: 0x600003449580>{number = 7, name = (null)}
    queue2 任务6 <NSThread: 0x600003449580>{number = 7, name = (null)}
    queue2 任务7 <NSThread: 0x600003449580>{number = 7, name = (null)}
    queue2 任务8 <NSThread: 0x600003449580>{number = 7, name = (null)}
    queue2 任务9 <NSThread: 0x600003449580>{number = 7, name = (null)}
    */        
    
    let queue1 = DispatchQueue.init(label: "串行队列1")
    let queue2 = DispatchQueue.init(label: "并发队列",attributes: .concurrent,target: nil)
    for i in 0..<10 {
        queue2.async { 
            print("queue2 任务\(i)",Thread.current)
        }
    }
    /*
    // 没有目标target队列 分配新队列
    queue2 任务0 <NSThread: 0x600002c41600>{number = 5, name = (null)}
    queue2 任务3 <NSThread: 0x600002c1d040>{number = 6, name = (null)}
    queue2 任务2 <NSThread: 0x600002c75bc0>{number = 4, name = (null)}
    queue2 任务1 <NSThread: 0x600002c44cc0>{number = 3, name = (null)}
    queue2 任务4 <NSThread: 0x600002c18380>{number = 7, name = (null)}
    queue2 任务5 <NSThread: 0x600002c10600>{number = 8, name = (null)}
    queue2 任务6 <NSThread: 0x600002c1d0c0>{number = 9, name = (null)}
    queue2 任务7 <NSThread: 0x600002c14140>{number = 10, name = (null)}
    queue2 任务8 <NSThread: 0x600002c187c0>{number = 11, name = (null)}
    queue2 任务9 <NSThread: 0x600002c1d140>{number = 12, name = (null)}
    */
    
    • 目标队列定义了块在何处运行,但它不会更改当前队列的语义。即使基础目标队列是并发的,提交到串行队列的块仍然按顺序执行。另外,您不能在没有并发的地方创建并发。如果一个队列及其目标队列都是串行的,则向两个队列提交块不会导致这些块同时运行。这些块仍按目标队列接收它们的顺序连续运行。

    • 调度队列从其目标队列继承了最低的qos

    • 针对Dispatch sources

    • 将事件处理和取消处理block提交到指定的目标队列。

    • 针对Dispatch I/O channels

    • 在指定的目标队列上执行I / O操作。 目标队列的qos会影响所得I / O操作的优先级。 例如,如果目标队列的qos为DispatchQoS.QoSClass.background,则当存在I / O争用时,将限制由该队列上的dispatch_io_read或dispatch_io_write执行的I / O操作。

    设置目标队列时,不要将队列A的目标设置为队列B,又将队列B的目标设置为队列A。

    • 下面代码queue2和queue3是串行队列,target是并发队列。将queue2和queue3的target设置成queue1,保证了queue2和queue3各自内部都是串行执行的,queue2和queue3之间是并发执行的。
    let queue1 = DispatchQueue.init(label: "并发队列1",attributes: .concurrent)
    let queue2 = DispatchQueue.init(label: "串行队列1",target: queue1)
    let queue3 = DispatchQueue.init(label: "串行队列3",target: queue1)
    
    for i in 0..<10 {
        queue2.async {
            print("queue2 任务\(i)")
        }
    
        queue3.async {
            print("*******queue3 任务\(i)")
        }
    }
    //输出
    /*
    queue2 任务0
    *******queue3 任务0
    queue2 任务1
    queue2 任务2
    *******queue3 任务1
    queue2 任务3
    *******queue3 任务2
    queue2 任务4
    queue2 任务5
    queue2 任务6
    queue2 任务7
    queue2 任务8
    queue2 任务9
    *******queue3 任务3
    *******queue3 任务4
    *******queue3 任务5
    *******queue3 任务6
    *******queue3 任务7
    *******queue3 任务8
    *******queue3 任务9
    */
    

    ————————————————
    原文链接:https://blog.csdn.net/u013756604/article/details/111224217

    —————————————————————————————————————————

    2 DispatchWorkItem 使用

    DispatchWorkItem是封装work的对象,可以监听work完成的通知,并指定和其他DispatchWorkItem之间的依赖关系。

    DispatchWorkItem结构

    DispatchWorkItem 是一个Class, 含有一个 实例变量表示任务是否被取消

    var isCancelled: Bool
    

    同时,你可以调用 DispatchWorkItem 的取消方法

    func cancel()
    Cancels the current work item asynchronously.
    
      1. 如果任务放入队列中,在任务执行之前调用了cancel(), 那么这个任务将不会被Queue执行
      1. 如果任务已经在执行中了, 调用了cancel(), 那么将直接终止任务的执行

    DispatchWorkItem 实践

    • 异步执行work
    let work1 = DispatchWorkItem {
        print("Simple Pure Work")
    }
    DispatchQueue.global().async(execute: work1)
    
    • 同步执行work
    let work1 = DispatchWorkItem {
       print("Simple Pure Work")
    }
    DispatchQueue.global().sync(execute: work1)
    
      1. 创建带QoS的workItem
    // 异步队列
    let work2 = DispatchWorkItem(qos: .background) {
          print("Work background(后台优先级最低\(Thread.current)")
    }
    
    let work3 = DispatchWorkItem(qos: .userInteractive) {
         print("Work userInteractive(最高优先级)\(Thread.current)")
    }
    
    DispatchQueue.global().async(execute: work2)
    DispatchQueue.global().async(execute: work3)
    /**
    Work userInteractive(最高优先级)<NSThread: 0x600001ee60c0>{number = 5, name = (null)}
    Work background(后台优先级最低<NSThread: 0x600001eefb00>{number = 4, name = (null)}
    */
    
      1. 栅栏函数
        注意,如果这里的queue是global队列,将无法达到效果
    //任务 2
    let work2 = DispatchWorkItem(qos: .default, flags: .enforceQoS) {
          print("work2")
    }
    // 任务 3
    let work3 = DispatchWorkItem(qos: .default, flags: .barrier) { // 类似栅栏函数
          Thread.sleep  (forTimeInterval: 3)
          print("work3")
    }
    // 任务 4
    let work4 = DispatchWorkItem(qos: .default) {
          print("work4")
    }
    // 队列 q
    let q = DispatchQueue(label: "concurrent", attributes: .concurrent)
          q.async(execute: work2)
          q.async(execute: work3)
          q.async(execute: work4)
            
    /** work2
        work3
        work4*/
    //work2执行完毕, 停留3秒, 在执行work4
    
      1. 创建带Flags的workItem
     /// 强制使用当前work的QoS
    let work2 = DispatchWorkItem(qos: .default, flags: .enforceQoS) {
          print("work2\(Thread.current)")
    }
    /// inheritQoS 继承队列的QoS
    let work3 = DispatchWorkItem(qos: .default, flags: .inheritQoS) {
          Thread.sleep(forTimeInterval: 3)
          print("work3\(Thread.current)")
    }
    /// work执行的时候使用当前的上下文?(具体有点不清晰)
    let work4 = DispatchWorkItem(qos: .default, flags: .assignCurrentContext) {
          print("work4\(Thread.current)")
    }
    
    let q = DispatchQueue(label: "concurrent", attributes: .concurrent)
          q.async(execute: work2)
          q.async(execute: work3)
          q.async(execute: work4)
    /**
    work2<NSThread: 0x600002d618c0>{number = 5, name = (null)}
    work4<NSThread: 0x600002d61e40>{number = 4, name = (null)}
    work3<NSThread: 0x600002d5cd80>{number = 6, name = (null)}*/
    
      1. WorkItem之间的同步,下面的两个任务将等待第一个任务5s后执行完才执行
    let work = DispatchWorkItem {
         print("work start\(Thread.current)")
         Thread.sleep(forTimeInterval: 5)
    }
    work.notify(queue: .main) {
         print("work done\(Thread.current)")
    }
    work.notify(queue: .global(), execute: DispatchWorkItem(block: {
         print("work item done\(Thread.current)")
    }))
    DispatchQueue.global().async(execute: work)
    /**
       work start<NSThread: 0x60000222ef00>{number = 6, name = (null)}
       work done<_NSMainThread: 0x6000022bc300>{number = 1, name = main}
       work item done<NSThread: 0x60000222ef00>{number = 6, name = (null)}
    */
    
      1. wait方法
    // wait()函数 等任务执行完毕, 在往下执行
    let queue = DispatchQueue(label: "queue", attributes: .concurrent)
    let workItem1 = DispatchWorkItem {
        sleep(3)
        print("workItem")
    }
            
    let workItem2 = DispatchWorkItem {
        sleep(2)
        print("workItem2")
    }
                
        queue.async(execute: workItem1)
        queue.async(execute: workItem2)
        print("before waiting")
        workItem1.wait() // 等待item1任务执行完, 执行后面任务
        workItem2.wait()
        print("after waiting")
         
    /* before waiting
       workItem2  
       workItem
       after waiting */
    

    原文链接 https://blog.csdn.net/m0_55782613/article/details/123117539

    相关文章

      网友评论

        本文标题:Swift补充- 多线程(DispatchQueue & Dis

        本文链接:https://www.haomeiwen.com/subject/ywzuirtx.html