iOS多线程

一、前言

本篇博文介绍的是iOS中常用的几个多线程技术:

NSThread

GCD

NSOperation

由于apple不提倡开发者直接对线程进行操作,日常开发过程中GCD和NSOperation的使用也较多,因此NSThread会介绍得少些,主要篇幅会放在后面两个。

二、NSThread

2.1 NSThread简介

NSThread是经过apple封装的面向对象的,它允许开发者直接以面向对象的思想对线程进行操作,没一个NSThread对象就代表一条线程,但是开发者必须手动管理线程的生命周期,这点是apple 不提倡的。如果开发者需要手动管理线程时apple提出了以下几点建议:

1、使用GCD和NSOperation

Writing thread-creation code manually is tedious and potentially error-prone and you should avoid it whenever possible. OS X and iOS provide implicit support for concurrency through other APIs

官方文档中说,由于手动管理线程很乏味,并且容易出错,所以apple建议你使用apple自家提供的GCD和NSOperation来进行多线程编程(apple心机婊,说好的手动管理线程的,怎么建议人家使用你的线程自动管理API了呢)。

2、停止长时间在idle状态的线程

remember that threads consume precious system resources.Threads use a nontrivial amount of memory, some of it wired, so releasing an idle thread not only helps reduce your application’s memory footprint, it also frees up more physical memory for other system processes to use

由于线程会占据大量的系统资源,适当的讲长时间处于挂起状态的线程停止,会释放更多的CPU和内存资源。

3、避免让多条线程同时处理一个数据

The simplest and easiest way to avoid thread-related resource conflicts is to give each thread in your program its own copy of whatever data it needs.

使每条线程都操作自己需要操作的数据,如果有必要的话将数据复制一份给另外一条线程处理。

...

如果你真的下定决心要自己管理线程,让Threading Programming Guide帮助你吧。

2.2 NSThread使用:

apple提供了两个方式来使用NSThread:

使用detachNewThreadSelector:toTarget:withObject:

NSThread.detachNewThreadSelector(Selector("thread"), toTarget:self, withObject:nil)

使用initWithTarget:selector:object:方法创建线程,但是必须手动启动:

let thread =NSThread(target:self, selector:Selector("thread") , object:nil)thread.start()

打印结果:

2016-03-23 16:51:51.238 Mutiple[9192:3103867] NSThread-(

"{number = 3, name = (null)}"

)

使用performSelectorInBackground: withObject:方法创建并自动启动

遗憾的是swift无法使用这个方法,原因:

The performSelector: method and related selector-invoking methods are not imported in Swift because they are inherently unsafe.

apple认为这个方法不是类型安全的。

2.3 NSThread几个常用的方法:

//获取主线程和当前线程:NSThread.mainThead()NSThread.currentThread()//退出,取消线程NSThread.exit()thread.cancel()//判断线程状态thread.executing//是否在运行thread.finished//是否结束thread.cancelled//是否取消//判断是否为多线程NSThread.isMultiThread()//使用通知根据线程状态做一些操作NSDidBecomeSingleThreadedNotification//变为单线程NSThreadWillExitNotification//线程即将结束NSWillBecomeMultiThreadedNotification//即将变为多线程//暂停线程

三、GCD

GCD是apple提出的为提高多核效率的多线程实现方案,

This technology takes the thread management code you would normally write in your own applications and moves that code down to the system level. All you have to do is define the tasks you want to execute and add them to an appropriate dispatch queue. GCD takes care of creating the needed threads and of scheduling your tasks to run on those threads

它在系统层级帮助开发者维护线程的生命周期,包括线程的创建、休眠、销毁等,开发者只需要关心需要实现的功能,将需要做的操作放到调度队列(dispatch queue)中,系统会根据线程的情况自动分配资源。

GCD引入了任务(task)和调度队列(dispatch queue)的概念。

所谓的任务,其实就是你要实现的功能和操作,即你要做什么;

调度队列,是实现功能的方式,简单说就是你想怎么做。

apple列举了dispatch queue的许多优点:

提供了更简洁的实现多线程的接口,让代码更简洁

帮开发者管理线程的生命周期

异步队列中不会造成死锁

...

队列中的所有任务都按照FIFO的顺序执行,GCD提供了三种队列:

串行队列

并行队列

主队列

3.1串行队列

创建

使用dispatch_queue_create(label: UnsafePointer, attr: dispatch_queue_attr_t)方法创建队列,来看看apple对方法中的两个属性的解释:

label:

A string label to attach to the queue to uniquely identify it in debugging tools such as Instruments, sample, stackshots, and crash reports. Because applications, libraries, and frameworks can all create their own dispatch queues, a reverse-DNS naming style (com.example.myqueue) is recommended. This parameter is optional and can be NULL.

label是队列的名称,apple推荐使用com.example.myQueue的规则来命名队列,用于debug的时候追踪队列以便于调试,可以为空

attr:

In OS X v10.7 and later or iOS 4.3 and later, specify DISPATCH_QUEUE_SERIAL (or NULL) to create a serial queue or specify DISPATCH_QUEUE_CONCURRENT to create a concurrent queue. In earlier versions, you must specify NULL for this parameter.

attr用于指定是串行队列还是并行队列,如果是NULL则默认串行队列

letserialQ1 = dispatch_queue_create("com.hah.serialQ1",DISPATCH_QUEUE_SERIAL)或者letserialQ2 = dispatch_queue_create("com.hah.serialQ2")

串行队列中的任务会按照FIFO的顺序依次执行,在同步执行下,系统不会开启新的线程,默认在主线程下执行任务,如果是在异步执行下,系统会根据线程资源情况决定是否开启新的线程。

GCD中可以通过串行队列进行锁线程

来看看同步情况下任务的执行情况:

同步串行:任务一个接着一个执行,不开启新线程;

func syncSerial() {NSLog("=======>>>syncSerial<<<========")      let q1 = dispatch_queue_create("q1",DISPATCH_QUEUE_SERIAL)dispatch_sync(q1) { () -> VoidinNSLog("1-%@",NSThread.currentThread());      }dispatch_sync(q1) { () -> VoidinNSLog("2-%@",NSThread.currentThread());      }dispatch_sync(q1) { () -> VoidinNSLog("3-%@",NSThread.currentThread());      }dispatch_sync(q1) { () -> VoidinNSLog("4-%@",NSThread.currentThread());      }dispatch_sync(q1) { () -> VoidinNSLog("5-%@",NSThread.currentThread()]);      }NSLog("=======>>>syncSerial<<<========")  }

打印结果:

2016-03-23 13:38:29.521 Mutiple[8199:2165063] =======>>>syncSerial<<<========

2016-03-23 13:38:29.522 Mutiple[8199:2165063] 1-(

"{number = 1, name = main}"

)

2016-03-23 13:38:29.522 Mutiple[8199:2165063] 2-(

"{number = 1, name = main}"

)

2016-03-23 13:38:29.522 Mutiple[8199:2165063] 3-(

"{number = 1, name = main}"

)

2016-03-23 13:38:29.523 Mutiple[8199:2165063] 4-(

"{number = 1, name = main}"

)

2016-03-23 13:38:29.523 Mutiple[8199:2165063] 5-(

"{number = 1, name = main}"

)

2016-03-23 13:38:29.523 Mutiple[8199:2165063]

=======>>>syncSerial<<<========

异步串行:任务一个接着一个执行,系统根据线程资源情况决定是否开启新的线程;

func asyncSerail() {NSLog("=======>>>asyncSerail<<<========")      let q1 = dispatch_queue_create("q1",DISPATCH_QUEUE_SERIAL)dispatch_async(q1) { () -> VoidinNSLog("1-%@",NSThread.currentThread());      }dispatch_async(q1) { () -> VoidinNSLog("2-%@",NSThread.currentThread());      }dispatch_async(q1) { () -> VoidinNSLog("3-%@",NSThread.currentThread());      }dispatch_async(q1) { () -> VoidinNSLog("4-%@",NSThread.currentThread());      }dispatch_async(q1) { () -> VoidinNSLog("5-%@",NSThread.currentThread());      }NSLog("=======>>>asyncSerail<<<========")  }

打印结果:

2016-03-23 13:04:26.713 Mutiple[7294:1959223] =======>>>asyncSerail<<<========

2016-03-23 13:04:26.714 Mutiple[7294:1959223] =======>>>asyncSerail<<<========

2016-03-23 13:04:26.715 Mutiple[7294:1959473] 1-(

"{number = 3, name = (null)}"

)

2016-03-23 13:04:26.716 Mutiple[7294:1959473] 2-(

"{number = 3, name = (null)}"

)

2016-03-23 13:04:26.716 Mutiple[7294:1959473] 3-(

"{number = 3, name = (null)}"

)

2016-03-23 13:04:26.716 Mutiple[7294:1959473] 4-(

"{number = 3, name = (null)}"

)

2016-03-23 13:04:26.716 Mutiple[7294:1959473] 5-(

"{number = 3, name = (null)}"

)

从打印可以看出,系统开启了新的线程,但是所有的任务都是在同一个线程里完成,避免在多个线程之间切换。

3.2 并行队列

并行队列,是全局队列的一种,它能处理并发任务。apple提供了四个已经创建好的并行队列,又叫全局队列(global queu),开发者只需要调用dispatch_get_global_queue( , )就能得到一个并行队列。并行队列同样按照FIFO的原则执行任务,它允许多个任务一起执行,同时执行的任务数量系统会根据情况决定。iOS5以后,apple也允许开发者自己创建并行队列。

3.2.1 获取全局队列:

调用dispatch_get_global_queue(identifier: Int, flags: UInt)方法获取全局队列:

两个参数:

identifier:

The quality of service you want to give to tasks executed using this queue. Quality-of-service helps determine the priority given to tasks executed by the queue. Queues that handle user-interactive or user-initiated tasks have a higher priority than tasks meant to run in the background.

identifier用于指定全局队列的优先级,有4种类型:

#defineDISPATCH_QUEUE_PRIORITY_HIGH2#defineDISPATCH_QUEUE_PRIORITY_DEFAULT0#defineDISPATCH_QUEUE_PRIORITY_LOW        (-2)#defineDISPATCH_QUEUE_PRIORITY_BACKGROUND  INT16_MIN

iOS 8以后可以通过:

QOS_CLASS_USER_INTERACTIVE,

QOS_CLASS_USER_INITIATED,

QOS_CLASS_UTILITY

QOS_CLASS_BACKGROUND.

来设置优先级。

从字面上就能看出,优先级的高低顺序为:DISPATCH_QUEUE_PRIORITY_HIGH>DISPATCH_QUEUE_PRIORITY_DEFAULT>DISPATCH_QUEUE_PRIORITY_LOW>DISPATCH_QUEUE_PRIORITY_BACKGROUND

需要特别说一下的是,系统建议将有关磁盘的I/0操作放到DISPATCH_QUEUE_PRIORITY_BACKGROUND优先级的队列中,防止占用过多的系统资源。

flags    :

Flags that are reserved for future use. Always specify 0 for this parameter.

flag默认是0

lethighQ = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH,0)//默认优先级也可以这么写:letdefaultQ = dispatch_get_global_queue(0,0)letdefaultQ = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0)letlowQ = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW,0)letbgQ = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND,0)

我写了段代码模拟了下优先级高的队列先执行的情况:

func queuePriority() {        let highQ = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH,0)        let defaultQ = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0)dispatch_async(defaultQ) { () -> VoidinNSLog("task3 start-%@",NSThread.currentThread());        }dispatch_async(highQ) { () -> VoidinNSLog("task1 start-%@",NSThread.currentThread());        }    }

按理说,应该是先执行task1,因为它优先级比较高,但是log是这样的:

2016-03-24 16:12:49.308 Mutiple[3815:2019744] task3 start-(

"{number = 3, name = (null)}"

)

2016-03-24 16:12:49.308 Mutiple[3815:2019743] task1 start-(

"{number = 4, name = (null)}"

)

优先级高的不先执行了,why?

我用优先级high和low做了实验,发现high优先级总是先执行,但是default为什么就不行呢?这个暂时还不明白。

3.2.2 手动创建并行队列

接下来手动创建一个并行队列,查看线程的创建情况:

同步并行:任务一个接着一个执行,系统默认不开启新线程;

func syncConcurrency() {NSLog("=======>>>syncConcurrency<<<========")      let q1 = dispatch_queue_create("q1",DISPATCH_QUEUE_CONCURRENT)dispatch_sync(q1) { () -> VoidinNSLog("1-%@",NSThread.currentThread());      }dispatch_sync(q1) { () -> VoidinNSLog("2-%@",NSThread.currentThread());      }dispatch_sync(q1) { () -> VoidinNSLog("3-%@",[NSThread.currentThread()]);      }dispatch_sync(q1) { () -> VoidinNSLog("4-%@",NSThread.currentThread());      }dispatch_sync(q1) { () -> VoidinNSLog("5-%@",NSThread.currentThread());      }NSLog("=======>>>syncConcurrency<<<========")  }

打印结果:

2016-03-23 13:40:16.968 Mutiple[8256:2181632] =======>>>syncConcurrency<<<========

2016-03-23 13:40:16.969 Mutiple[8256:2181632] 1-(

"{number = 1, name = main}"

)

2016-03-23 13:40:16.970 Mutiple[8256:2181632] 2-(

"{number = 1, name = main}"

)

2016-03-23 13:40:16.970 Mutiple[8256:2181632] 3-(

"{number = 1, name = main}"

)

2016-03-23 13:40:16.970 Mutiple[8256:2181632] 4-(

"{number = 1, name = main}"

)

2016-03-23 13:40:16.970 Mutiple[8256:2181632] 5-(

"{number = 1, name = main}"

)

2016-03-23 13:40:16.970 Mutiple[8256:2181632] =======>>>syncConcurrency<<<========

从打印输出可以看出,所有任务都是在主线程中完成,因为同步执行,即使开了多个线程,下一个的任务还是要等待当前任务执行完毕才能开始执行,开多个线程之后造成资源浪费。就像iOS多线程浅汇-原理篇中讲到的,如果这个时候开了三条线程,系统就必须在三条线程之间切换,除此之外,每条线程都有自己的栈和寄存器,三条线程就有三组的栈和寄存器被复制和替换,这相对于只有一条线程来说,效率肯定是大大降低的。

异步并行:多个任务一起执行,顺序不固定,但是可以通过dispatch_group和NSOperation添加依赖控制任务执行顺序。

func asyncConcurrency() {NSLog("=======>>>asyncConcurrency<<<========")      let q1 = dispatch_queue_create("q1",DISPATCH_QUEUE_CONCURRENT)dispatch_async(q1) { () -> VoidinNSLog("1-%@",NSThread.currentThread());      }dispatch_async(q1) { () -> VoidinNSLog("2-%@",NSThread.currentThread();      }dispatch_async(q1) { () -> VoidinNSLog("3-%@",NSThread.currentThread());      }dispatch_async(q1) { () -> VoidinNSLog("4-%@",NSThread.currentThread());      }dispatch_async(q1) { () -> VoidinNSLog("5-%@",NSThread.currentThread());      }NSLog("=======>>>asyncConcurrency<<<========")  }

打印结果:

2016-03-23 13:30:20.987 Mutiple[8107:2109033] =======>>>asyncConcurrency<<<========

2016-03-23 13:30:20.988 Mutiple[8107:2109033] =======>>>asyncConcurrency<<<========

2016-03-23 13:30:20.989 Mutiple[8107:2109139] 5-(

"{number = 7, name = (null)}"

)

2016-03-23 13:30:20.989 Mutiple[8107:2109119] 3-(

"{number = 4, name = (null)}"

)

2016-03-23 13:30:20.989 Mutiple[8107:2109093] 1-(

"{number = 3, name = (null)}"

)

2016-03-23 13:30:20.989 Mutiple[8107:2109118] 2-(

"{number = 5, name = (null)}"

)

2016-03-23 13:30:20.990 Mutiple[8107:2109138] 4-(

"{number = 6, name = (null)}"

)

异步下创建多个串行队列实现并行效果

func multiAsyncSerail() {NSLog("=======>>>asyncSerail<<<========")      let q1 = dispatch_queue_create("q1",DISPATCH_QUEUE_SERIAL)dispatch_async(q1) { () -> VoidinNSLog("1-%@",NSThread.currentThread());      }              let q2 = dispatch_queue_create("q2",DISPATCH_QUEUE_SERIAL)dispatch_async(q2) { () -> VoidinNSLog("2-%@",NSThread.currentThread());      }              let q3 = dispatch_queue_create("q3",DISPATCH_QUEUE_SERIAL)dispatch_async(q3) { () -> VoidinNSLog("3-%@",NSThread.currentThread());      }              let q4 = dispatch_queue_create("q4",DISPATCH_QUEUE_SERIAL)dispatch_async(q4) { () -> VoidinNSLog("4-%@",NSThread.currentThread());      }      let q5 = dispatch_queue_create("q5",DISPATCH_QUEUE_SERIAL)dispatch_async(q5) { () -> VoidinNSLog("5-%@",NSThread.currentThread());      }NSLog("=======>>>asyncSerail<<<========")  }

打印结果:

2016-03-23 13:33:21.412 Mutiple[8150:2128078] =======>>>asyncSerail<<<========

2016-03-23 13:33:21.413 Mutiple[8150:2128078] =======>>>asyncSerail<<<========

2016-03-23 13:33:21.414 Mutiple[8150:2128320] 4-(

"{number = 7, name = (null)}"

)

2016-03-23 13:33:21.414 Mutiple[8150:2128308] 1-(

"{number = 3, name = (null)}"

)

2016-03-23 13:33:21.414 Mutiple[8150:2128312] 3-(

"{number = 5, name = (null)}"

)

2016-03-23 13:33:21.414 Mutiple[8150:2128321] 5-(

"{number = 6, name = (null)}"

)

2016-03-23 13:33:21.414 Mutiple[8150:2128311] 2-(

"{number = 4, name = (null)}"

)

小结:

使用GCD实现多线程时:

同步串行和同步并行:都会按照顺序依次执行任务,且不会开启新的线程。

异步串行:会开启新的线程,但是系统默认在同一线程内按照任务顺序依次执行,因为这个时候开了多个线程也要顺序执行任务,开线程只会造成资源浪费

异步并行:开启多个线程,有多个任务同时执行,系统会根据每条线程的情况分配,执行顺序不固定。

在异步串行情况下,可以通过创建多个串行队列,实现异步并行的功能,让多个任务同时进行。

3.3 主队列

The main dispatch queue is a globally available serial queue that executes tasks on the application’s main thread.

主队列是一个全局性的串行队列,用于执行app中主线程的任务,对run loop起作用,将隶属于run loop的事件源与队列任务的执行交织在一起(官方文档翻译过来的,太拗口了。。。)。主队列是一个对于app来说十分重要的同步点,凡是与UI和用户反馈相关的都必须交给主队列完成。这个队列是系统自动创建的,开发者只需要获取队列,并执行操作即可。

获取主线程的方法:

letmainQueue = dispatch_get_main_queue()

最经常用的:

let queue = dispatch_get_global_queue(0,0)dispatch_async(queue,{()-> Void in  //在这里做一些耗时的操作  dispatch_async(dispatch_get_main_queue(),{()-> Void in      //刷新UI  })})

3.4 队列组(dispatch group)

Dispatch groups are a way to block a thread until one or more tasks finish executing.Groups provide a useful synchronization mechanism for code that depends on the completion of other tasks

使用队列组能够实现让一个或多个任务执行完毕之后再执行下一个任务。

Another way to use dispatch groups is as an alternative to thread joins.

使用队列组将多个任务连接在一起。

用队列组可以在特定的场景下实现一些特定的功能,十分有趣:

加入有三个任务:taks1,task2,taks3。task1与task2没有什么特殊关系,task3需要等待task1和task2结束之后才能开始执行。

使用队列组实现任务等待

func dispatchGroup() {      let group = dispatch_group_create()      let queue1 = dispatch_get_global_queue(0,0)      let queue2 = dispatch_get_global_queue(0,0)      let queue3 = dispatch_get_global_queue(0,0)      dispatch_group_async(group, queue1) { () ->Voidinforiin0..<3{              NSLog("task1 - %d",i)          }      }      dispatch_group_async(group, queue2) { () ->Voidinforiin0..<3{              NSLog("task2 - %d",i)          }      }      dispatch_group_wait(group, DISPATCH_TIME_FOREVER)      NSLog("wait completed")      dispatch_group_async(group, queue3) { () ->Voidinforiin0..<3{              NSLog("task3 - %d",i)          }      }  }

log输出:

2016-03-24 22:23:25.717 2016-03-24 22:42:42.465 MultipleThradDemo[49112:4109966] task2 - 0

2016-03-24 22:42:42.465 MultipleThradDemo[49112:4109967] task1 - 0

2016-03-24 22:42:42.467 MultipleThradDemo[49112:4109966] task2 - 1

2016-03-24 22:42:42.467 MultipleThradDemo[49112:4109967] task1 - 1

2016-03-24 22:42:42.468 MultipleThradDemo[49112:4109966] task2 - 2

2016-03-24 22:42:42.468 MultipleThradDemo[49112:4109967] task1 - 2

2016-03-24 22:42:42.468 MultipleThradDemo[49112:4109901] wait completed

2016-03-24 22:42:42.469 MultipleThradDemo[49112:4109967] task3 - 0

2016-03-24 22:42:42.469 MultipleThradDemo[49112:4109967] task3 - 1

2016-03-24 22:42:42.469 MultipleThradDemo[49112:4109967] task3 - 2

dispatch_group_wait(group: dispatch_group_t, timeout:dispatch_time_t):括号里面的第一个参数group为等待的队列组,第二个timeout为等待的时间

使用队列组的通知功能实现上述场景:

func groupNotify() {      let group = dispatch_group_create()      let queue1 = dispatch_get_global_queue(0,0)      let queue2 = dispatch_get_global_queue(0,0)      let queue3 = dispatch_get_global_queue(0,0)      dispatch_group_async(group, queue1) { () ->Voidinforiin0..<3{              NSLog("task1 - %d",i)          }      }      dispatch_group_async(group, queue2) { () ->Voidinforiin0..<3{              NSLog("task2 - %d",i)          }      }      dispatch_group_notify(group, queue3) { () ->VoidinNSLog("task1、 task2 completed")foriin0..<3{              NSLog("task3 - %d",i)          }      }  }

log输出:

2016-03-24 23:05:29.010 MultipleThradDemo[49493:4127151] task2 - 0

2016-03-24 23:05:29.010 MultipleThradDemo[49493:4127148] task1 - 0

2016-03-24 23:05:29.011 MultipleThradDemo[49493:4127148] task1 - 1

2016-03-24 23:05:29.011 MultipleThradDemo[49493:4127151] task2 - 1

2016-03-24 23:05:29.011 MultipleThradDemo[49493:4127148] task1 - 2

2016-03-24 23:05:29.011 MultipleThradDemo[49493:4127151] task2 - 2

2016-03-24 23:05:29.012 MultipleThradDemo[49493:4127151] task1、 task2 completed

2016-03-24 23:05:29.012 MultipleThradDemo[49493:4127151] task3 - 0

2016-03-24 23:05:29.012 MultipleThradDemo[49493:4127151] task3 - 1

2016-03-24 23:05:29.012 MultipleThradDemo[49493:4127151] task3 - 2

3.5 GCD的线程安全

尽管GCD本身就是线程安全的,但是为了更好的使用GCD,apple还是给了几个建议:

Do not call the dispatch_sync function from a task that is executing on the same queue that you pass to your function call

在本队列调用dispatch_sync,再将本队列传入同步方法会造成死锁。

我想apple 的意思应该是这样的:

func deadLock() {//let q = dispatch_get_global_queue(0, 0)let q = dispatch_queue_create("come.multiThread.serialQ", DISPATCH_QUEUE_SERIAL)dispatch_async(q) { () -> VoidinNSLog("1 start-%@",[NSThread.currentThread()]);dispatch_sync(q, { () -> VoidinNSLog("2 start-%@",[NSThread.currentThread()]);            })NSLog("3 start-%@",[NSThread.currentThread()]);        }NSLog("4 start-%@",[NSThread.currentThread()]);    }

但是我发现会不会造成死锁和当前队列是串行还是并行有关。

如果是串行队列,同步执行会阻塞线程,而且串行队列一次只能执行一个任务,q里面等待的任务与同步阻塞的任务是同一个任务,造成死锁。

如果是并行队列,一次能执行多个任务,即使同步阻塞了线程,由于可以多个任务一起执行,q里面没有任务等待,不会造成死锁。

stackoverflow上类似问题的回答:

Usingdispatch_sync()to enqueue a Block to the serial queue on which you're already running is guaranteed to deadlock.Usingdispatch_sync()to enqueue a Block to theconcurrentqueue on which you're already running is quite likely to deadlock.

派发同步任务到在正在执行的串行队列肯定会造成死锁;派发到并行队列也有很大的可能造成死锁。所以上面的例子中,并行队列虽然没有造成死锁,还是有风险的。

note:往主线程派发同步任务也会造成死锁

func mainThreadLock() {        let main = dispatch_get_main_queue()NSLog("start-%@",[NSThread.currentThread()]);dispatch_sync(main) { () -> VoidinNSLog("1 -%@",[NSThread.currentThread()]);        }NSLog("end-%@",[NSThread.currentThread()]);    }

打印输出:

2016-03-25 10:15:46.638 Mutiple[15813:5558093] start-(

"{number = 1, name = main}"

)

同步任务与主线程任务相互等待,造成死锁。

Avoid taking locks from the tasks you submit to a dispatch queue.If you need to synchronize parts of your code, use a serial dispatch queue instead of a lock.

尽量不用自己锁线程,否则得到的结果可能会十分出乎你的意料。使用GCD实现多线程时,apple建议用串行队列替代线程锁。

文/nuclear(简书作者)

原文链接:http://www.jianshu.com/p/0f9f7ed53b48

著作权归作者所有,转载请联系作者获得授权,并标注“简书作者”。

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

推荐阅读更多精彩内容

  • 一、前言 上一篇文章iOS多线程浅汇-原理篇中整理了一些有关多线程的基本概念。本篇博文介绍的是iOS中常用的几个多...
    nuclear阅读 1,989评论 6 18
  • 一、前言 多线程这个词对于大家来说并不陌生,但是真正能够熟知多线程的坑点只在少数,iOS中并行也一直被认为是恶魔,...
    和珏猫阅读 287评论 0 0
  • 1. GCD简介 什么是GCD呢?我们先来看看百度百科的解释简单了解下概念 引自百度百科:Grand Centra...
    千寻_544f阅读 340评论 0 0
  • 在新宿车站送完朋友回家,风有些吵闹,我冷的慌,只一个劲的向前走,风儿委委屈屈的在我身后哭泣:“你为什么要走那么快,...
    Jieun阅读 726评论 0 1
  • 上次回家,在同一天内乘坐了重庆地铁和成都地铁,产生了深思。 在成都地铁上我看见的,大多数是妆容精致,穿着潮流的人群...
    红橙夏雨阅读 278评论 4 2