Swift补充- 多线程(DispatchQueue & DispatchWorkItem )

目录

  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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,219评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,363评论 1 293
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,933评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,020评论 0 206
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,400评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,640评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,896评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,597评论 0 199
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,327评论 1 244
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,581评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,072评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,399评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,054评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,083评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,849评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,672评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,585评论 2 270

推荐阅读更多精彩内容