原文:http://www.appcoda.com/grand-central-dispatch/

译者:xuq (看源文件)

为了更清晰的表达,相关技术词汇不会被翻译,欢迎指正。

中央处理单元(cpu)自存在以来最大的改进之一是包含了多核,并在多核上运行多线程,这意味着cpu在特定时刻可以同时运行多个任务。

只能串行执行任务或伪多线程多年前就已成为历史,串行? 伪多线程是什么鬼? 如果你听过这些,说明你是个老腊肉并且有接触过旧式电脑,或者你亲自在旧式电脑上用过旧系统。但是不论

cpu有几核或它有多强,如果你不使用它这些优点的话一切都是毫无意义,正是意识到这点,多任务处理和多线程编程慢慢进入人们的视野,开发者不仅可以,而且应该在实际开发中尽可

的将程序分成能够在多个线程上并发执行的代码块来充分发挥cpu的多任务处理能力。

多核多线程编程好处有很多:更快的执行,更好的用户体验,界面更流程等等。 想象下你在`main thread`选择下载多张图片,然后整个界面直接被卡死了,60秒后终于下载完成,然后你欣慰的看到了下载的图片,这样的应用还能让人愉悦的玩耍么?

在iOS中,苹果提供了两种方法来进行多任务处理:`Grand Central dispatch`(以下简称`GCD`)和`NSOperationQueue`框架。 当你需要在`非main thread`或`非 main queue`上运行相关任务时,它俩都能非常完满的完成任务。要用哪个,这个你自己来决定,本篇教程我们只探讨`GCD`的用法。在开始之前我们先了解一条任意时刻都必须遵守的规则:**主线程用于界面更新和用户交互,任何耗时或者耗cpu的任务必须在concurrent queue或者background queue上运行**。小白可能很难领会为什么要这么做,老鸟告诉你记住并遵守就好了。

`GCD`首次在iOS 4中引入,在实现并发,高性能和并发任务中表现出良好的灵活性和可配置性。但在`Swift3`之前它都跟天书一样,与`swift`格格不入的古董C语言风格,晦涩难记的方法名都让你望而却步,码农们宁愿用`NSOperaionQueue`都不用`GCD`,稍微的搜索了解下你就会明白有多糟糕。

`Swift3`中`GCD`的用法发生了巨大变化,全新的`Swift`风格语法上手更简单,正是这些变更驱使我写下这篇教程来介绍`Swift3`下关于`GCD`的日常用法和注意事项,如果你用过旧语法的`GCD`(即使就使用了那么一点),那新语法你也一样可以驾轻就熟。

在正式开始前,首先来了解下`GCD`的一些基本知识:

1. `dispatch queue`: 最基本也最重要,其实就是一堆在主线程(或后台线程)上同步(或异步)来执行的代码,一旦被创建出来,操作系统就开始接手管理,在cpu上分配时间片来执行队列内的代码。开发者没法参与`queue`的管理。队列采用`FIFO模式`(先进先出),意味着先加入的也会被先完成,可以想象下超市排队买单, 队伍前的总是最先买单出去,后续会有示例来详细说明(译者说明:对于`serial queue`来说的确是这样)。

2. `work item`:一段代码块,可以在queue创建的时候添加,也可以单独创建方便之后复用,你可以把它当成将要在`queue`上运行的一个代码块。`work items`也遵循着`FIFO模式`,也可以同步(或异步)执行,如果选择同步的方式,运行中的程序直到代码块完成才会继续之后的工作,相对的,如果选择异步,运行中的程序在触发了代码块就立刻返回了。后续也会有示例来详细说明。

3. `serial`(串行)vs`concurrent`(并行):`serial`将会执行完一个任务才会开始下一个,`concurrent`触发完一个就立即进入下一个,而不管它是否已完成。

如果你要在`main queue`上运行代码,那你要提高警惕,不能让代码块的运行占用了界面响应更新的时间片。重申下规则:**任何时候你都应该在主线程上更新UI**。如果你尝试在其他线程更新,实际情况是你将不知道界面会不会更新或者什么时候被更新????。虽然如此,在更新ui前,你还是可以将更新UI前的相关准备工作在后台先运行完毕,例如你可以在`background queue`上下载图片数据,完成后你回到主线程来显示图片。

大部分情况下,你不用自己创建`queue`,系统已经帮你创建好了一些常用的全局`queue`,你可以直接使用它们来处理你想要运行的任务,你可能会关心队列将会在哪个线程上运行,你是否能够指定线程来执行代码神马的,实际情况是除了创建、配置、使用`queue`其他你什么都做不了。 iOS管理着了一个线程池,意味着除了主线程还有一堆其他线程存在着,系统将会选择其中的一个或多个(依赖于你创建了多少个queue以及你创建queue的方式),具体选择哪个开发者是不知道的,操作系统会根据当前并发的任务,处理器的负载情况来做具体决定,但你真的想自己去管理这些么?????????

测试环境

为了更好的展示GCD的使用,我们本打算建立多个很小但很针对的示范工程,playground本来是个非常完美的解决方案,但是因为playground不支持从不同线程上调用方法,最后只好妥协使用了一个普通的项目工程,你可以在这里下载它[https://github.com/appcoda/GCDSamples/raw/master/Starter_Project.zip]它

除了以下两点,整个工程项目几乎为空:

1. 在`ViewController.swift`文件中你将会发现一系列没有被实现的方法,每个方法将会给我们演示一个GCD的特性,你需要做的就是在viewDidLoad(_:)方法中对代码取消注释以便执行相关的方法。

2. 在`Main.storyboard`,`ViewController`场景中你会发现添加了一个`UIImageView`,并且它已经通过`IBOutlet`和`ViewController`相关联,之后我们会在一个模拟真实使用场景的demo中使用到它。

正式进入话题:


dispatch Queues入门

在Swift3中,创建`dispatch queue`方式如下:

1
letqueue=dispatchQueue(label: "com.appcoda.myqueue" )

只需给`queue`提供一个唯一标签,获取唯一标签的一个很有效的做法是将你的dns地址反序,如(“com.appcoda.myqueue”),这也是苹果推荐的做法。但这不是硬性要求,你可以使用任何唯一的字符串作为标签。label不是创建`queue`的唯一参数,在后续介绍相关内容时会逐一讲到其他参数。

一旦队列创建完成,我们就可以用它来运行代码,你可以使用`sync`来同步运行或使用`async`来异步运行。 为了更简洁的演示,我们会先使用block(闭包)来提供可执行代码,后续会使用`dispatchWorkItem`对象来代替block(注意:block也可当成`Queue`里的一个`dispatchWorkItem`, 译者补充: dispatchWorkItem 的初始方法:init(qos:,flags:,block:), block作为它其中的一个初始参数)。这里我们只是简单的在`queue`中同步的打印0~9

为了在控制台清晰的区分结果,我们添加了一个红点,在后续添加更多队列或任务执行时,带颜色的点可以帮助我们更好的区分不同`queue`里的任务。

将上述代码片段放入你建立的初始项目的`ViewController.swift`文件的`simpleQueues()`方法中,确保这个方法在`viewDidAppear(_:)`中没有被注释,然后运行它,查看Xcode控制台,你将会看到一堆数字被打印出来,单个queue输出并不能帮助我们理解GCD是怎么工作,为了对比,我们在`simpleQueues()`方法的block后面添加另外一个打印100~109的代码块(仅仅为了展示区别)

作为对比,红点代码块在`background Queue`上运行,而蓝点代码块将会在`main queue`上运行,观察运行结果你会发现程序被卡住了,直到红点代码块执行执行完毕,`main queue`上的蓝点代码块才会被继续执行,之所以如此是因为`queue`中代码是同步执行的,控制台输出如下:

如果使用`async`方法会发生神马呢?它会让`queue`中的代码异步执行吗? 如果是这样的话,程序应该不会像上面结果那样卡住而应该在`queue`内任务都执行完成前返回主线程上执行第二个`for`循环,使用`async`方法更新下`queue`的运行方式:

现在,来看下Xcode的控制台


相比于`sync`执行结果,`async`显示的更加有趣,你会发现`main queue`的代码(第二个`for`循环)和我们`queue`在并发执行,自定义`queue`在开始得到了更多运行时间,但是这仅仅是因为优先级(后续会讲)。 通过这个示例我们搞懂了几件事:

1. 使用async主线程和后台线程可以并行执行任务

2. 使用sync则只能串行执行任务,当前线程被卡住直到串行任务完成才继续

尽管示例非常简单,但它完美的展示了`queue`中的代码块是怎么同步或异步执行的。 我们也会将在后续的示例中继续采用上例中色彩缤纷的打印日志(特定的颜色代码特定`queue`中代码执行结果,不同颜色代表不同的`queue`).

服务等级(QoS)和优先级

在使用GCD和dispatchQueues的时候我们经常需要告知操作系统App里的哪些任务比其他任务更重要,哪些优先级更高。`main queue`通常被用来处理UI和响应操作,它拥有很高的优先级。 实际使用场景中,通过向系统提供QoS信息,iOS会根据你的配置合理的处理优先级并提供所需资源(如cpu执行时间),虽然最终所有的任务都将完成,但优先级会让一部分早完成、一部分晚完成。

确定任务重要和优先级的属性被称为GCD服务等级(GCD QoS),实际上,QoS是个基于具体场景的枚举类型,在初始队列时,可以提供合适的QoS参数来得到相应的权限,如果没有指定QoS,那么初始方法会使用队列提供的默认的QoS值,QoS所有可用选型可以查看[这里],开始之前,建议你仔细阅读,以下会概括的介绍下可用的QoS场景(QoS Case),或者称为QoS等级(QoS classes),从前到后,优先级从高到低:

  • userInteractive

  • userInitiated

  • default

  • utility

  • background

  • unspecified

回到Xcode项目,找到`queuesWithQoS()`方法,定义并初始化以下两个全新的`dispatch queues`:

1
2
letqueue1=dispatchQueue(label: "com.appcoda.queue1" ,qos:dispatchQoS.userInitiated)
letqueue2=dispatchQueue(label: "com.appcoda.queue2" ,qos:dispatchQoS.userInitiated)

注意,两个队列都使用相同的QoS等级,所以执行时它们拥有相同权限。 像之前做的那样,第一个队列包括一个`for`循环来显示0~9(带红点),第二个队列将执行另外一个`for`循环来显示100~109(带蓝点)。

查看下拥有相同权限(相同QoS)的队列执行结果

-记得对viewDidAppear(_:)中的queuesWithQos()方法取消注释

![qos_result](http://www.appcoda.com/wp-content/uploads/2016/11/t57_3_sample3_qos_same-1024x520.png)

不难发现,两个队列的任务被"均匀"的执行了,这正是我们所期待的结果 现在,如下图所示将`queue2`的QoS等级改为更低的utility

~~~swift

let queue2 = dispatchQueue(label: "com.appcoda.queue2",qos: dispatchQoS.utility)

~~~

查看结果:

![utility](http://www.appcoda.com/wp-content/uploads/2016/11/t57_4_sample4_qos_utility-1024x496.png)

毫无悬念,优先级更高的`queue1`比`queue2`更快被执行,虽然在`queue1`运行的时候`queue2`得到一个运行的机会,系统还是将资源倾斜给了被标记为更重要的`queue1`,等`queue1`内的任务全部被执行完成,系统才开始全心全意服务于`queue2` 。

现在开始做另外一个实验: 这次将第一个队列QoS等级改为background:

let queue1 = dispatchQueue(label: "com.appcoda.queue1",qos: dispatchQoS.background)

让我们看下使用background这个次低的优先级会发生什么:

![background](http://www.appcoda.com/wp-content/uploads/2016/11/t57_5_sample5_qos_background-1024x574.png)

这次因为utility QoS比background QoS优先级更高,第二个队列更快被完成。

通过上述例子,我们了解了QoS等级的使用方法,但是如果我们同时在主线程执行一个任务会发生什么? 在方法的后面添加以下代码:

for i in 1000..<1010 {

print("??",i)

}

同时赋予第一个队列更高的权限

let queue1 = dispatchQueue(label: "com.appcoda.queue1",qos: dispatchQoS.userInitiated)

运行结果如下:

![resullt](http://www.appcoda.com/wp-content/uploads/2016/11/t57_6_sample6_qos_mainqueue-1024x664.png)

实践证明: `main queue`默认就有一个很高的权限(译者注:实际运行发现也只是utility????),`queue1`和`main queue`并发执行,`queue2`在其他两个队列运行时没有得到多少运行的机会,最后才完成 。

# 并发队列(Concurrent Queues)

目前为止,我们已经看到dispatch queues是怎样同步和异步工作以及服务等级是怎么影响系统服务的优先等级,可能你会发现,上述所有示例中所有的`queue`都是串行执行的(serial),意味着如果你分配多个任务给任意一个队列,这些任务将会一个接一个被完成,而不是同时完成。在这小节中,我们将会展示怎样让多任务(多工作项)同时运行,或者换种说法,怎么创建一个并发的队列。

回到项目,找到`concurrentQueues()`方法(记得取消对该方法的注释) ,在这个新方法里创建一个如下的新队列:

let anotherQueue = dispatchQueue(label: "com.appcoda.anotherQueue",qos: .utility)

接下来,在队列中分配任务(或者称工作项):

![xx](http://www.appcoda.com/wp-content/uploads/2016/11/code-snippet-3-1024x663.png)

结果如下所示,任务被顺序的执行:

![xx](http://www.appcoda.com/wp-content/uploads/2016/11/t57_7_sample7_serial_queue-1024x683.png)

接下来,修改`anotherQueue`队列初始化方法:

let anotherQueue = dispatchQueue(label: "com.appcoda.anotherQueue",qos: .utility,attributes: .concurrent)

上述代码中添加了一个新参数`attributes`,当这个参数被赋予`concurrent`时,这个队列所有的任务将被同时执行。 如果你不加这个参数,那么队列默认就是串行执行的(serial)。 QoS不是必填项,在初始的时候完全可以忽略它。

再次运行代码,可以发现任务被高度并发执行了:

![xxx](http://www.appcoda.com/wp-content/uploads/2016/11/t57_8_sample8_concurrent_queue-1024x584.png)

注意到更改Qos等级同样可以影响到任务的执行,无论如何,只要你初始了一个并发队列,那么并发执行的任务会被同等对待,所有任务都将获得被执行的机会

`attrubites`参数可以选择另外一个值:`initiallyInactive`。 通过定义为`initiallyInactive` 队列任务不会自动开始执行,需要开发者主动去触发。 为了展示这个特性,需要建一个`inactiveQueue `队列:

var inactiveQueue: dispatchQueue!

初始化一个队列,然后赋值给`inactiveQueue `

let anotherQueue = dispatchQueue(label: "com.appcoda.anotherQueue",qos: .utility,attributes: .initiallyInactive)

inactiveQueue = anotherQueue

为了让`concurrentQueues()`这个方法外的其他方法也可以 这个示例中的`inactiveQueue `属性非常必要,因为`anotherQueue`的作用域仅在方法中。 当方法执行完毕,它就对应用不可见了,根本不可能被方法外的其他方法来使用。(译者注:这里写的不太好,如果对象可能为空, 应该果断用optional,而不应该使用!再去判断是否空,inactiveQueue 应该被声明为optional类型:`var inactiveQueue: dispatchQueue?`)

再次运行应用,你将发现根本没有输出,这是我们所期待的结果,通过在`viewDidAppear(_:)`方法中添加一下代码来触发队列的运行:

if let queue = inactiveQueue {

queue.activate()

dispatchQueue类的`activate()`让任务执行,因为队列没有标记为并发,所以它将顺序执行:

![xx](http://www.appcoda.com/wp-content/uploads/2016/11/t57_9_sample9_inactive_serial-1024x596.png)

现在问题来了:,怎么创建一个既是初始不活跃又是并发的队列? 很简单,与之前提供`attributes`一个参数不同,这次给它赋值为一个包含两者的数组

let anotherQueue = dispatchQueue(label: "com.appcoda.anotherQueue",qos: .userInitiated,attributes: [.concurrent,.initiallyInactive])

结果如下:

![xx](http://www.appcoda.com/wp-content/uploads/2016/11/t57_10_sample10_inactive_concurrent-1024x650.png)

# 延迟执行

有时你需要应用的某个流程中的某项任务延迟执行,GCD允许你执行某个方法来达到特定时间后运行你指定任务的目的。

这次我们要使用的是初始项目中的`queueWithDelay()`方法,首先添加以下代码:

~~~~swift

let delayQueue = dispatchQueue(label: "com.appcoda.delayqueue",qos: .userInitiated)

print(Date())

let additionalTime: dispatchTimeInterval = .seconds(2)

~~~~

一开始像平常那样创建一个`dispatchQueue`供后续代码使用;然后我们打印当前时间用于对比延迟任务对比。 延迟时间通常是个添加在`dispatchTime`值后面的`dispatchTimeInterval ` 枚举(值类型为整型) ,用于说明延迟详情(后续会讲到)。在示例中,任务设置为延迟两秒执行,这里使用的是`seconds`方法,除了这个,还提供了其他时间选项可供选择:

* milliseconds (毫秒,千分之一秒)

* microseconds (微秒,千分之一毫秒)

* nanoseconds (纳秒,千分之一微秒)

说明完毕,开始使用队列:

delayqueue.asyncAfter(deadline: .Now() + additionalTime) {

print(Date())

`Now()`方法返回当前时间,在此基础上添加我们想要延迟的时间,运行代码,控制台将输出如下结果:

![xx](http://www.appcoda.com/wp-content/uploads/2016/11/t57_11_sample11_delay-1024x464.png)

确实,正如预期那样,队列的任务2秒后才被执行。注意如果你不想使用一些预定义的方法来指定等待时间,你还有另外一种选择,直接在当前时间后直接添加一个`Double`值:

delayqueue.asyncAfter(deadline: .Now() + 0.75) {

在这种情况下,任务将在0.75秒后被执行。 你可以不适用`Now()`方法,但是你必须自行提供一个dispatchTime值,以上展示的就是在队列中实现最简单的延迟任务,实际上这也是你所需要掌握的延迟实现方法的全部内容。

#访问主队列和全局队列

前面的例子的`queue`都是通过手工创建得到,实际使用中并不总要你自己来做这个,特别是你不想改变队列的属性,在博文开始的地方我提到了系统会创建一系列的被称为全局队列的后台队列。 你可以像自己创建的队列一样使用它们,只要不要太滥用就行。

访问一个全局队列方法如下:

let globalQueue = dispatchqueue.global()

你可以像我们之前使用自定义队列那样使用它:

![xx](http://www.appcoda.com/wp-content/uploads/2016/11/code-snippet-4-1024x232.png)

使用全局变量,你只能使用部分属性,例如服务等级(Qos class)

~~~swfit

let globalQueue = dispatchqueue.global(qos: .userInitiated)

如果你不指定该参数(像第一个用例那样),那么Qos属相将采用默认的`default`值

抛开全局队列,你毋庸置疑会经常使用到主队列,最有可能是更新UI的时候。 接下来的代码块将展示在其他队列中运行主队列,根据你的需要,你可以指定同步或异步来执行你的代码:

dispatchqueue.main.async {

// Do something

在Xcode里,当你输入`dispatchqueue.main.`(记住最后有个.),Xcode将会自动提示所有你能在主队列中访问的方法(实际上这个技巧适应于所有队列,操作方法是在队列名称后带`.`),上述代码就是主线程你所需要知道的内容,你也可以像自己创建队列那样,添加延迟执行代码块

接下来将实操下主线程怎么更新UI,初始项目的`Main.storyboard`中有个`ViewController`场景包含了一个图片视图,通过`IBOutlet`属性连接到了`ViewController`类中,在`ViewController`类中找到`fetchImage()`方法, 补充上下载Appcoda logo和在图片视图中展示它的代码,如下所示(这里不讨论`URLSession`类和它的使用方法,自行脑补吧):

func fetchImage() {

let imageURL: URL = URL(string: "http://www.appcoda.com/wp-content/uploads/2015/12/blog-logo-dark-400.png")!

(URLSession(configuration: URLSessionConfiguration.default)).dataTask(with: imageURL,completionHandler: { (imageData,response,error) in

if let data = imageData {

print("Did download image data")

self.imageView.image = UIImage(data: data)

}

}).resume()

你会发现代码中并没有在主线程中更新UI,而是在后台线程中运行`dataTask()`方法,编译运行下程序结果如下(记得调用`fetchImage()`方法):

![xx](http://www.appcoda.com/wp-content/uploads/2016/11/t57_12_update_UI_bg.png)

虽然我们已经下载了图片并打印了相关信息,因为ui没有更新导致图片也没有显示出来,最有可能发生的情况是图片在打印信息的一段时间后显示(如果还有其他任务在运行,可能会要等更久),但是问题不止这一个,控制台输出了一大串关于在后台线程中更新ui的警告日志。

现在纠正到主线程来更新UI,将相关代码更新如下:

if let data = imageData {

print("Did download image data")

dispatchqueue.main.async {

self.imageView.image = UIImage(data: data)

}

重新运行下app,因为主队列被唤起并更新了UI,图片在下载后立即就显示出来了。

# 使用dispatchWorkItem对象

dispatchWorkItem是一个代码块,它可以被分到任何的队列,包含的代码可以在后台或主线程中被执行,简单来说:它被用于替换我们前面写的代码block来调用

最简单的使用WorkItem的例子如下:

let workItem = dispatchWorkItem {

接下来展示一个小示例来阐述`dispatchWorkItem`的使用方法,找到`useWorkItem()`方法,在其中添加如下代码:

func useWorkItem() {

var value = 10

let workItem = dispatchWorkItem {

value += 5

workItem用于让value每次递增5,如下代码所示,通过调用`workItem`的`perform()`方法来激活

workItem.perform()

默认将会在主线程执行,但你可以选择在其他队列中执行,比如:

let queue = dispatchqueue.global()

queue.async {

workItem.perform()

这样也一样会运行正常。为了更简洁的实现同样目的,`dispatchQueue`提供了另外一个方法:

queue.async(execute: workItem)

当一个WorkItem被分发,你可以通知主队列(或其他队列)来做一些后续的处理:

workItem.notify(queue: dispatchqueue.main) {

print("value = ",value)

以上代码将会在控制台打印`value`对象的值,它将在workItem被分发后被调用,整理下代码,使用workItem的完整用法如下:

let queue = dispatchqueue.global(qos: .utility)

queue.async(execute: workItem)

workItem.notify(queue: dispatchqueue.main) {

print("value = ",value)

以下是运行结果(`useWorkItem()`已经在`viewDidAppear(_:)`中被调用了:

![xx](http://www.appcoda.com/wp-content/uploads/2016/11/t57_13_dispatch_work_item.png)

#总结

这篇博文包含的内容应该满足大部分你将要使用到多任务和并发编程的场景,但也没有那么全面包含了GCD的方方面面,可能博文中有引到但是没有具体说明的内容,总体来说,我希望博文能够尽量的简单,从而被广大开发者所理解和接受。 如果你平常都没有用到GCD,那你应该考虑使用GCD来为你的`main queue`减负,主动承担一些比较重的操作,能在后台运行的任务绝不在`main queue`上运行。 浅显易懂的GCD能够非常有效的加快应用的运行和响应速度,你值得拥有。

本文demo下载地址见[GitHub](https://github.com/appcoda/GCDSamples)

---

译者杂耍时间:

####Q1: 为啥代码中蓝点没有比红点先执行?

func simpleQueues() {

// 根据苹果的说法: 不同的queue之间应该是并发的,互不影响

// 如果将queue的优先级设置为比主线程低,理论上来说主线程上应该先执行,queue应该后执行

// 苹果原文如下: You can create as many serial queues as you need,and each queue operates concurrently with respect to all other queues. In other words,if you create four serial queues,each queue executes only one task at a time but up to four tasks Could still execute concurrently,one from each queue

let queue = dispatchQueue(label: "com.appcoda.myqueue",qos: .background)

queue.sync {

for i in 0..<10 {

print("????",i)

for i in 100..<110 {

print("??",250)"> // 运行结果:

???? 0

???? 1

???? 2

???? 3

???? 4

???? 5

???? 6

???? 7

???? 8

???? 9

?? 100

?? 101

?? 102

?? 103

?? 104

?? 105

?? 106

?? 107

?? 108

?? 109

####A1: 为了简洁说明,去掉了for循环,答案如下:

let queue = dispatchQueue(label: "com.appcoda.myqueue",250)"> queue.sync {

print("????",0)

print("\(Thread.current)")

print("??",250)"> 运行结果:

{number = 1,name = main}

?? 0

懵逼了,好好的全局后端队列,怎么跑到主线程上去执行了. 答案就是苹果为了优化性能,sync会尽可能在当前线程来运行

>原文:As an optimization,this function invokes the block on the current thread when possible.

原文出处:[https://developer.apple.com/reference/dispatch/dispatchqueue/1452870-sync](https://developer.apple.com/reference/dispatch/dispatchqueue/1452870-sync)

####Q2: 探讨下崩溃问题:

// 说明:当前在主线程上执行

print("????",250)"> dispatchQueue.main.sync {

// 然后程序崩溃了:

Thread 1:EXC_BAD_INSTRUCTION(code=EXC_i386_INVOP,...)

好冤枉啊,我不就想明明白白的在主线程上同步的运行么,为啥说崩就崩了?

####A2: 答案是死锁了

主线程是串行的,上个任务执行完成才会继续下个任务,`simpleQueues()`整个方法相当于mainQueue的一个任务(任务A),现在它里面加了个sync的{任务A1},意味着任务A1只有等任务A完成才能开始,但是要完成任务A的话就必须先完成任务A1,然后A又在等A1,然后就傻逼了,逻辑好绕吧????....

>原文:

Submits a block to a dispatch queue for synchronous execution. Unlike dispatch_async(_:_:),this function does not return until the block has finished. Calling this function and targeting the current queue results in deadlock.

原文出处: [https://developer.apple.com/reference/dispatch/dispatchqueue/1452870-sync](https://developer.apple.com/reference/dispatch/dispatchqueue/1452870-sync)

Swift3使用GCD和DispatchQueues的更多相关文章

  1. 在iOS应用程序中使用dispatch_source_t无法在GCD块中运行计时器

    我想在GCD块中创建一个定时器来将其用作后台任务.但是,当我看到定时器火从来没有.这是我的代码:那么这是什么问题?解决方法您必须使您的dispatch_source_t成为一个类属性或实例变量,因此它不会超出范围.如果你这样做,你的代码可以正常工作,例如:另请注意,如果您希望在启动后台进程后更改BOOL的值,您可能也希望将其设置为类属性,如上所示.我也将其更名为观察消息,使其目的更为简单.

  2. UIWebView stringByEvaluatingJavaScriptFromString在使用GCD调用时挂在iOS5.0 / 5.1上

    我在viewDidLoad中有以下代码,它在iOS4.3上正常工作,但它挂在iOS5/5.1上.在iOS5/5.1上,显示警告对话框,但无法关闭,UI线程冻结,OK按钮无法单击.这是一个bug吗?解决方法经过测试,我认为它是一个Bug,并改变使用的代码会解决

  3. ios – 从NSOperationQueue获取底层dispatch_queue_t

    解决方法没有这样的事情,NSOperationQueue和dispatch_queue_t没有一一对应的关系,两个API中的排队概念是非常不同的.NSOperationQueue用于执行代码的唯一调度队列是全局并发队列的默认优先级.

  4. GCD并发队列? (iOS 4.2.1)

    Iam有问题:在iOS4.2.1(设备)上的concurrentQueue为零,但是在运行iOS5.0.1的另一台设备上,相同的代码可以完美工作.当我检查标题时,它表示从iOS4.0起可用,我做错了什么?下面的代码从互联网中获取图像,在4.2.1之后的所有内容中都能很好的工作,但是没有在4.2.1中,任何想法为什么?您可以使用GCD创建并发队列吗?

  5. ios – 使用GCD和webView进行死锁

    我发现了一个似乎导致WebKit陷入僵局的问题.如果我从我的主线程运行此代码,我正确地看到一个警报.我可以点击警报上的“确定”按钮,它会解散并且一切正常:如果我进行了一些修改,那么仍然会出现警告消息,但是无法点击“确定”按钮–您无法关闭警报,如果您闯入应用程序,它将挂在stringByEvaluatingJavaScriptFromString调用中:这两者中唯一不同的是,在第二个中,它在调度队列

  6. ios – 如何在Swift中实现非常精确的计时?

    我正在开发一个具有琶音/排序功能的音乐应用程序,需要很高的计时准确性.目前,使用“Timer”我已经达到了平均抖动约为5ms的精度,但最大抖动约为11ms,这对于8号,16号音符的快速琶音来说是不可接受的.特别是第32条.我已经读过’CAdisplayLink’比’Timer’更准确,但由于它的准确度限制在1/60秒,看起来它的准确性不如我用Timer实现了.潜入CoreAudio是实现我想要的唯一途径吗?还有其他方法可以实现更精确的计时吗?

  7. 在Swift中应用Grand Central Dispatch(上

    在这两篇教程中,你会学到GCD的来龙去脉。起步libdispatch是Apple所提供的在IOS和OSX上进行并发编程的库,而GCD正是它市场化的名字。Swift中的闭包和OC中的块类似甚至于他们几乎就是可交换使用的。但OC中的块可以安全的替换成Swift中的闭包。再一次,这完全取决于GCD。QoS等级表示了提交任务的意图,使得GCD可以决定如何制定优先级。QOS_CLASS_USER_INteraCTIVE:userinteractive等级表示任务需要被立即执行以提供好的用户体验。

  8. 在Swift中应用Grand Central Dispatch 下

    通过使用dispatch_barrrier和dispatch_sync,你做到了让PhotoManager单例在读写照片时是线程安全的。还有,使用dispatch_async异步执行cpu密集型任务,从而为视图控制器初始化过程减负。幸运的是,dispatchgroups就是专为监视多个异步任务的完成情况而设计的。调度组调度组在一组任务都完成后会发出通知。在组内所有事件都完成时,GCDAPI提供了两种方式发送通知。打开PhotoManager.swift,替换downloadPhotosWithComple

  9. 转 Grand Central Dispatch 基础教程:Part 1/2 -swift

    第一节将解释什么是GCD并了解几个GCD的基础函数。GettingStartedGCD是libdispatch的代名词,libdispatch代表着运行iOS与OSX的多核设备上执行并行代码的官方代码库。再有一点要记住的就是在任何GCD文档中涉及到Objective-C的块代码都是可以用Swift的闭包来替换的。举个具有线性安全性的代码示例leta=["thread-safe"]。因为,这一切都是由GCD控制的。任务的开始执行的时间完全由GCD决定。它也是唯一一个用作向UIView对象发送信息或推送监听。

  10. 【荐】Grand Central Dispatch Tutorial for Swift: Part 1/2

    所有的dispatchqueues自身都是线程安全的。dispatch_sync把任务添加到对应队列并等待其完成后再继续执行当前任务,容易造成死锁,或阻塞当前任务。dispatch_after指定时间后把任务添加到队列中。效果就像是延时后的dispatch_async。而array和dictionary在swift中是以struct的形式实现的,所以以上的读操作返回的是一个副本。在GCD中使用dispatchbarrier来解决这个问题。dispatchbarrier是一组方法,它们都已顺序化的方式来结合

随机推荐

  1. Swift UITextField,UITextView,UISegmentedControl,UISwitch

    下面我们通过一个demo来简单的实现下这些控件的功能.首先,我们拖将这几个控件拖到storyboard,并关联上相应的属性和动作.如图:关联上属性和动作后,看看实现的代码:

  2. swift UISlider,UIStepper

    我们用两个label来显示slider和stepper的值.再用张图片来显示改变stepper值的效果.首先,这三个控件需要全局变量声明如下然后,我们对所有的控件做个简单的布局:最后,当slider的值改变时,我们用一个label来显示值的变化,同样,用另一个label来显示stepper值的变化,并改变图片的大小:实现效果如下:

  3. preferredFontForTextStyle字体设置之更改

    即:

  4. Swift没有异常处理,遇到功能性错误怎么办?

    本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容,请发送邮件至dio@foxmail.com举报,一经查实,本站将立刻删除。

  5. 字典实战和UIKit初探

    ios中数组和字典的应用Applicationschedule类别子项类别名称优先级数据包contactsentertainment接触UIKit学习用Swift调用CocoaTouchimportUIKitletcolors=[]varbackView=UIView(frame:CGRectMake(0.0,0.0,320.0,CGFloat(colors.count*50)))backView

  6. swift语言IOS8开发战记21 Core Data2

    上一话中我们简单地介绍了一些coredata的基本知识,这一话我们通过编程来实现coredata的使用。还记得我们在coredata中定义的那个Model么,上面这段代码会加载这个Model。定义完方法之后,我们对coredata的准备都已经完成了。最后强调一点,coredata并不是数据库,它只是一个框架,协助我们进行数据库操作,它并不关心我们把数据存到哪里。

  7. swift语言IOS8开发战记22 Core Data3

    上一话我们定义了与coredata有关的变量和方法,做足了准备工作,这一话我们来试试能不能成功。首先打开上一话中生成的Info类,在其中引用头文件的地方添加一个@objc,不然后面会报错,我也不知道为什么。

  8. swift实战小程序1天气预报

    在有一定swift基础的情况下,让我们来做一些小程序练练手,今天来试试做一个简单地天气预报。然后在btnpressed方法中依旧增加loadWeather方法.在loadWeather方法中加上信息的显示语句:运行一下看看效果,如图:虽然显示出来了,但是我们的text是可编辑状态的,在storyboard中勾选Editable,再次运行:大功告成,而且现在每次单击按钮,就会重新请求天气情况,大家也来试试吧。

  9. 【iOS学习01】swift ? and !  的学习

    如果不初始化就会报错。

  10. swift语言IOS8开发战记23 Core Data4

    接着我们需要把我们的Rest类变成一个被coredata管理的类,点开Rest类,作如下修改:关键字@NSManaged的作用是与实体中对应的属性通信,BinaryData对应的类型是NSData,CoreData没有布尔属性,只能用0和1来区分。进行如下操作,输入类名:建立好之后因为我们之前写的代码有些地方并不适用于coredata,所以编译器会报错,现在来一一解决。

返回
顶部