asyn gcd swift
2016-03-12 17:54:00 weixin_34013044 阅读数 1

调度队列(Dispatch Queues)

GCD提供了调度队列(Dispatch Queues)来处理被提交的任务,这些队列负责管理你提交给GCD的任务并且按照先进先出(FIFO)的顺序执行它们。调度队列有以下3种:

  • 主队列(Main queue)
    提交的任务执行在主线程(main thread),串行(Serial)队列,用来更新UI。

  • 并行队列(Concurrent Queues)
    提交的任务按先进先出(FIFO)出列,并发执行在不同的线程上。任何一个点上执行的任务数量是变化的,具体由系统条件决定,并且任务完成先后顺序也是任意的。

  • 串行队列(Serial queues)
    串行队列一次只行一个任务,无论是同步(dispatch_sync)还是异步(dispatch_async)提交任务给串行队列,都遵循FIFO原则。

注意:提交到队列中的任务是串行还是并行执行,由队列本身决定。


获取队列的方式

  1. 创建队列
//创建串行队列
let serialQ = dispatch_queue_create("serialQ", DISPATCH_QUEUE_SERIAL)
//创建并发队列
let concurrentQ = dispatch_queue_create("concurrentQ", DISPATCH_QUEUE_CONCURRENT)
  1. 获取主线程中的主队列(Main queue)
    因为主线程只有一个,所有这自然是串行队列(Serial queues)。一切跟UI有关的操作必须放在主线程中执行。
let mainQ = dispatch_get_main_queue()
  1. 获取系统的全局队列
    系统也提供了一些全局并发队列(The Global Concurrent Queues)。这些队列与它们自己的服务质量(Qos)类有关,服务质量(Qos)类向GCD提供了被提交任务的意图以便GCD更好的决定它们的执行顺序。
  • QOS_CLASS_USER_INTERACTIVE: user interactive类代表那些为了提供良好的用户体验而必须立即被完成的任务。一般用于更新UI,事件处理和执行时间短的,工作量小的任务。
  • QOS_CLASS_USER_INITIATEDuser initiated类代表那些UI被触发引起并且能够异步执行(dispatch_async)的任务。一般用在当用户在等待立即返回结果的时候或者那些需要进一步用户交互的任务。
  • QOS_CLASS_UTILITYutility 类代表那些长时间执行的任务,例如用户可见的进度指示条。一般用在计算,I/O操作,网络等类似的任务。这个类被设计得很高效。
  • QOS_CLASS_BACKGROUNDbackground类代表那些用户并不直接关注的任务。一般用来预取数据,维护和那些没有用户交互或者没有时间要求的任务。

值得注意的是,在获取全局队列的时候,dispatch_get_global_queue方法也可以指定优先权值。当指定优先权值时,这些权值会对应合适的Qos类:

import Foundation

//指定Qos类
let globalUserInteractiveQ = dispatch_get_global_queue(QOS_CLASS_USER_INTERACTIVE, 0)
let globalUserInitiatedQ   = dispatch_get_global_queue(QOS_CLASS_USER_INITIATED, 0)
let globalUtilityQ         = dispatch_get_global_queue(QOS_CLASS_UTILITY, 0)
let globalBackgroundQ      = dispatch_get_global_queue(QOS_CLASS_BACKGROUND, 0)

//指定优先权值
let priorityHighQ          = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0)
let priorityDefaultQ       = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)
let priorityLowQ           = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0)
let priorityBackgroundQ    = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0)

print(globalUserInteractiveQ.description)
print(globalUserInitiatedQ.description)
print(globalUtilityQ.description)
print(globalBackgroundQ .description)
print("-------------------------------------------------------------")
print(priorityHighQ.description)
print(priorityDefaultQ.description)
print(priorityLowQ.description)
print(priorityBackgroundQ.description)

运行程序,得到结果如下:


1572381-f61a6e84dc95e1be.png

从运行结果我们不难看出:

DISPATCH_QUEUE_PRIORITY_HIGH  = QOS_CLASS_USER_INITIATED
DISPATCH_QUEUE_PRIORITY_LOW   = QOS_CLASS_UTILITY
DISPATCH_QUEUE_PRIORITY_BACKGROUND = DISPATCH_QUEUE_PRIORITY_BACKGROUND

至于default Qos,Apple官方做了如下描述:

The priority level of this QoS falls between user-initiated and utility. This QoS is not intended to be used by developers to classify work. Work that has no QoS information assigned is treated as default, and the GCD global queue runs at this level.


执行UI关联任务

UI关联的任务只能在主线程中执行,所以主队列(main queue)是唯一的选择。向主队列中提交任务的正确方法就是用dispatch_async方法。

注意:不能用dispatch_sync方法向main queue中提交任务,因为会造成主线程无限期的阻塞并使程序陷入死锁。

import UIKit

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
       
        let mainQ = dispatch_get_main_queue()
        
        dispatch_async(mainQ){
             print("Current thread = \(NSThread.currentThread())")
             print("Main thread = \(NSThread.mainThread())")
        }
        
        dispatch_async(mainQ){[weak self] in
            let alertController = UIAlertController(title: "GCD", message: "GCD is amazing!",
            preferredStyle: .Alert)
            alertController.addAction(UIAlertAction(title: "OK",
            style: .Default,
            handler: nil))
            self!.presentViewController(alertController, animated: true,
            completion: nil)
        }
    }
}

运行程序会输出:


1572381-63fb0b8b9da7f66e.png

执行UI无关的任务

对于任何与UI无关的任务,可以使用全局并发队列(The Global Concurrent Queues)。全局并发队列允许同步或者异步提交任务。但是同步提交的任务并不意味着程序要等待同步代码块执行完成才能继续,而是指并发队列会等待当前的代码块执行完成才会继续执行队列里面的下一个代码块。

注意:当你将代码块放进并发队列时,程序不会等待队列执行代码块而是直接继续。这是因为并发队列是在其他线程里面执行代码而非主线程。(有一个例外:当用dispatch_sync 方法提交一个任务到并发队列或者串行队列时,ios可能的话,会在当前线程去执行任务,而这个当前线程很可能是主线程。)

下面例子来说明:

import UIKit
class ViewController: UIViewController {
    
    func printFrom1To10(){
        for counter in 0..<10{
            print("Counter = \(counter) - Thread = \(NSThread.currentThread())")
        }
    }

    override func viewDidLoad() {
        super.viewDidLoad()
       
        let queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)

            dispatch_sync(queue, printFrom1To10)
            dispatch_sync(queue, printFrom1To10)
    }
}

运行程序,输出结果如下:


1572381-c85aa93bcf1a15d6.png

注意到我们将printFrom1To10方法以同步的方式提交了2次到全局并发队列,可是结果却出乎意料。可以看出任务在主线程中以串行的方式执行了。这就是因为dispatch_sync方法会优先使用当前的线程。Apple对此有如下说明:

GCD Reference: As an optimization, this function invokes the block on the current thread when possible.

下面我们看一个从网络上下载图片并且用UIImageView显示的例子

  1. 异步(dispatch_async)方式向并发队列提交一个代码块。
  2. 在上面提交的代码块中,用同步(dispatch_sync)的方式再次向这个并发队列提交一个代码块,用来下载图片。在一个异步的代码块中用同步的方式去下载图片只会堵塞用同步方法提交任务的队列,对主线程并无影响。从主线程来看,整个操作其实还是异步的。我们关心的是在下载的时候不会阻塞主线程。
  3. 下载完成后,我们用同步的方式向主队列提交显示图片的任务。
import UIKit

class ViewController: UIViewController {
    

    override func viewDidLoad() {
        super.viewDidLoad()
       
        let concurrentQ = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)

        dispatch_async(concurrentQ) { [weak self] in
            
            var image:UIImage?
            
            print("1.异步提交任务线程:"+NSThread.currentThread().description)
            
            dispatch_sync(concurrentQ) { _ in
                
                print("2.下载图片线程:"+NSThread.currentThread().description)
                
                let url = NSURL(string:"https://github.com/SmileMelody/images/raw/master/image1.png")
                let urlRequest = NSURLRequest(URL: url!)
                image = UIImage(data: try! NSURLConnection.sendSynchronousRequest(urlRequest, returningResponse: nil))
                
            
            dispatch_sync(dispatch_get_main_queue()){ _ in
       
                print("3.更新UI线程:"+NSThread.currentThread().description)
                
                let imageView = UIImageView(frame: self!.view.bounds)
                imageView.contentMode = .ScaleAspectFit
                imageView.image = image
                imageView.backgroundColor = UIColor.redColor()
                self!.view.addSubview(imageView)
                
            }
       } 
    }
}
}

执行程序,结果如下

1572381-bcc94e979856f967.gif

很显然,图片下载完成之后,才开始执行更新UI。同时线程1和线程2是同一个线程,这是因为我们前面提到的dispatch_sync这个方法会优先使用当前线程。而当前线程就是线程1,因为下载图片的任务是在线程1中提交的。这么做的原因就是在图片完全下载后才更新UI,同时不阻塞主线程

注意:这里的NSURLConnection.sendSynchronousRequest方法也是同步的方法。如果用异步的方法那么在图片还未下载完全之前,就可能执行更新UI的任务。

NSURLConnection.sendSynchronousRequest方法在IOS9里面已经被弃用,而实际上我们下载图片更新UI也不需要上面这么麻烦:

import UIKit

class ViewController: UIViewController {
    

    override func viewDidLoad() {
        super.viewDidLoad()
      
        let url = NSURL(string:"https://github.com/SmileMelody/images/raw/master/image1.png")
        let task = NSURLSession.sharedSession().dataTaskWithURL(url!) { data,response,error in
            
            if data?.length > 0 && error == nil {
                dispatch_async(dispatch_get_main_queue()){ [weak self] in
                    let imageView = UIImageView(frame: self!.view.bounds)
                    imageView.contentMode = .ScaleAspectFit
                    imageView.image = UIImage(data: data!)
                    imageView.backgroundColor = UIColor.redColor()
                    self!.view.addSubview(imageView)
                }
            }
        }
        
        task.resume()
    }
 
}

延迟执行任务(dispatch_after)

import UIKit

class ViewController: UIViewController {
    
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        let delayInSeconds = 4.0
        let delayInNanoSeconds = dispatch_time(DISPATCH_TIME_NOW, Int64(delayInSeconds * Double(NSEC_PER_SEC)))
        let concurrentQ = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)
        
        dispatch_after(delayInNanoSeconds, concurrentQ){ _ in
           /* Perform your operations here */
        }
        
    }
}

只执行一次(dispatch_once)

import Foundation

var token: dispatch_once_t = 0
var numberOfEntries = 0

func executedOnlyOnce(){
    numberOfEntries++
    print("Executed \(numberOfEntries) time(s)")
}

dispatch_once(&token, executedOnlyOnce)
dispatch_once(&token, executedOnlyOnce)

结果:


1572381-9d9741a337c3a0d9.png

任务组合(dispatch_group)

dispatch_group_create:创建一个调度任务组
dispatch_group_async:异步提交任务到一个任务组
dispatch_group_notify:监听任务组中所有任务执行完毕后再执行,不阻塞当前线程
dispatch_group_wait:等待指定时间直到所有任务完成,阻塞当前线程

模拟提交三个任务

import UIKit

class ViewController: UIViewController {
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        let taskGroup = dispatch_group_create()
        let concurrentQ = dispatch_get_global_queue(QOS_CLASS_DEFAULT, 0)
        
        dispatch_group_async(taskGroup, concurrentQ) { () -> Void in
            print("task_1")
        }
        
        dispatch_group_async(taskGroup, concurrentQ) { () -> Void in
            
            print("task_2")
        }
        
        dispatch_group_async(taskGroup, concurrentQ) { () -> Void in
            
            print("task_3")
        }
        
        dispatch_group_notify(taskGroup, concurrentQ) { () -> Void in
            print("all done")
        }    
    }
}

输出为:

task_1
task_3
task_2
all done

dispatch_group_enter & dispatch_group_leave:

import UIKit

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        
        let taskGroup = dispatch_group_create()
        let serialQ = dispatch_queue_create("", DISPATCH_QUEUE_SERIAL)
        let concurrentQ = dispatch_queue_create("", DISPATCH_QUEUE_CONCURRENT)
        
        for index in 1...3{
            dispatch_group_enter(taskGroup)
            downLoadImage(index, queue: serialQ)
            dispatch_group_leave(taskGroup)
        }
        
        dispatch_group_enter(taskGroup)
        dispatch_async(concurrentQ) { _ in
            sleep(4)
            print("俺是插队的任务。")
        }
        
        dispatch_group_leave(taskGroup)
        
        
        dispatch_group_notify(taskGroup, serialQ) { () -> Void in
             print("任务完成!")
        }
        
    }

    func downLoadImage(num:Int,queue:dispatch_queue_t){
        dispatch_async(queue) { _ in
            sleep(2)
            print("任务:"+"\(num)")
        }
    }
}

输出结果:

任务:1
俺是插队的任务。
任务:2
任务:3
任务完成!

指定次数提交同一个任务(dispatch_apply)

import UIKit

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        
        let taskGroup = dispatch_group_create()
        let serialQ = dispatch_queue_create("", DISPATCH_QUEUE_SERIAL)
        let concurrentQ = dispatch_queue_create("", DISPATCH_QUEUE_CONCURRENT)
        
       
        dispatch_group_enter(taskGroup)
        dispatch_async(concurrentQ) { _ in
            sleep(4)
            print("俺是插队的任务。")
        }
        
        dispatch_group_leave(taskGroup)
        
        //指定次数提交相同任务到队列中
        dispatch_apply(3, serialQ, downLoadImage)
        
       
        //注意这里只是监听了serialQ里面的任务
        dispatch_group_notify(taskGroup, serialQ) { () -> Void in
             print("任务完成!")
        }
        
    }

    func downLoadImage(var num:Int = 1){
        sleep(2)
        print("任务:"+"\(num++)")
    }
}

输出为:

任务:0
任务:1
俺是插队的任务。
任务:2
任务完成!

信号量

dispatch_semaphore_create:用于创建信号量,可以指定初始化信号量的值
dispatch_semaphore_wait:判断信号量,如果为1,则往下执 行,信号量减1。如果是0,则等待
dispatch_semaphore_signal:代表运行结束,信号量加1

import UIKit

class ViewController: UIViewController {
    
    let semaphore = dispatch_semaphore_create(2)
    var count = 1
    
    func doSomething(){
        
        if dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER) == 0{
            sleep(3)
            print("第一次"+"\(count++)"+"执行")
            
            dispatch_semaphore_signal(semaphore)
        }
    }

    override func viewDidLoad() {
        super.viewDidLoad()
        
        let concurrentQ = dispatch_queue_create("", DISPATCH_QUEUE_CONCURRENT)
        
        for _ in 1...8{
        
            dispatch_async(concurrentQ) { () -> Void in
            
                self.doSomething()
            }
        
        }
    }
}

可以清楚的看见每次只有两个线程执行了:


1572381-ab85e9ad646f83dd.gif

dispatch barriers

就如同它的名字一样,在队列执行的任务中增加“栅栏”,在增加“栅栏”之前已经开始执行的block将会继续执行,当dispatch_barrier_async开始执行的时候其他的block处于等待状态,dispatch_barrier_async的任务执行完后,其后的block才会执行。


1572381-2d8ec95918e2445b.png
import UIKit

class ViewController: UIViewController {

    func writeToFile(flag:Int){
        NSUserDefaults.standardUserDefaults().setInteger(flag, forKey: "barrier")
    }
    
    func readFromFile(){
        print(NSUserDefaults.standardUserDefaults().integerForKey("barrier"))
    }

    override func viewDidLoad() {
        super.viewDidLoad()
        
        let concurrentQ = dispatch_queue_create("", DISPATCH_QUEUE_CONCURRENT)
        writeToFile(1)
        
        dispatch_async(concurrentQ){self.readFromFile()}
        dispatch_async(concurrentQ){self.readFromFile()}
        dispatch_async(concurrentQ){self.readFromFile()}
        dispatch_barrier_async(concurrentQ){self.writeToFile(7);self.readFromFile()}
        dispatch_async(concurrentQ){self.readFromFile()}
        dispatch_async(concurrentQ){self.readFromFile()}
    }
    
}

转载于:https://www.jianshu.com/p/66eb7e2a8b0f

2017-07-04 15:40:00 weixin_34128501 阅读数 15

Objective-c 中的我们常用的GCD如何在Swift中使用呢?例如:dispatch_async,dispatch_after etc.

  • Global
DispatchQueue.global(qos: .userInitiated).async {
 }

为何用.userInitiated 代替 DISPATCH_QUEUE_PRIORITY请参考苹果文档

  • Main
 DispatchQueue.main.async {
        
  }
  • After
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { // in half a second...
    print("Are we there yet?")
}
  • Custom
let queue = DispatchQueue(label: "com.zhuo.my-serial-queue",
                           attributes: [.serial, .qosUtility])
    func doStuff() {
        queue.async {
            print("Hello")
        }
    }

下载图片

DispatchQueue.global(qos: .userInitiated).async {
    let image = self.loadAnImage()
    // To the main thread to update the UI
    DispatchQueue.main.async {
        self.imageView.image = image
    }
}
2014-12-12 21:02:14 u011723466 阅读数 430

转自:http://blog.csdn.net/zhangao0086/article/details/38904923   Bannings的专栏

Grand Central Dispatch(GCD)是异步执行任务的技术之一。一般将应用程序中记述的线程管理用的代码在系统级中实现。开发者只需要定义想执行的任务并追加到适当的Dispatch Queue中,GCD就能生成必要的线程并计划执行任务。由于线程管理是作为系统的一部分来实现的,因此可统一管理,也可执行任务,这样就比以前的线程更有效率。


Dispatch Queue

Dispatch Queue是用来执行任务的队列,是GCD中最基本的元素之一。

Dispatch Queue分为两种:

  • Serial Dispatch Queue,按添加进队列的顺序(先进先出)一个接一个的执行
  • Concurrent Dispatch Queue,并发执行队列里的任务
简而言之,Serial Dispatch Queue只使用了一个线程,Concurrent Dispatch Queue使用了多个线程(具体使用了多少个,由系统决定)。 
可以通过两种方式来获得Dispatch Queue,第一种方式是自己创建一个:

let myQueue: dispatch_queue_t = dispatch_queue_create("com.xxx"nil) 

第一个参数是队列的名称,一般是使用倒序的全域名。虽然可以不给队列指定一个名称,但是有名称的队列可以让我们在遇到问题时更好调试;当第二个参数为nil时返回Serial Dispatch Queue,如上面那个例子,当指定为DISPATCH_QUEUE_CONCURRENT时返回Concurrent Dispatch Queue。

需要注意一点,如果是在OS X 10.8或iOS 6以及之后版本中使用,Dispatch Queue将会由ARC自动管理,如果是在此之前的版本,需要自己手动释放,如下: 

let myQueue: dispatch_queue_t = dispatch_queue_create("com.xxx"nil)

dispatch_async(myQueue, { () -> Void in

    println("in Block")

})

dispatch_release(myQueue) 

以上是通过手动创建的方式来获取Dispatch Queue,第二种方式是直接获取系统提供的Dispatch Queue。

要获取的Dispatch Queue无非就是两种类型:

  • Main Dispatch Queue
  • Global Dispatch Queue / Concurrent Dispatch Queue
一般只在需要更新UI时我们才获取Main Dispatch Queue,其他情况下用Global Dispatch Queue就满足需求了:

//获取Main Dispatch Queue

let mainQueue = dispatch_get_main_queue()

//获取Global Dispatch Queue

let globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT0)

得到的Global Dispatch Queue实际上是一个Concurrent Dispatch Queue,Main Dispatch Queue实际上就是Serial Dispatch Queue(并且只有一个)。
获取Global Dispatch Queue的时候可以指定优先级,可以根据自己的实际情况来决定使用哪种优先级。
一般情况下,我们通过第二种方式获取Dispatch Queue就行了。

dispatch_after

dispatch_after能让我们添加进队列的任务延时执行,比如想让一个Block在10秒后执行: 

var time = dispatch_time(DISPATCH_TIME_NOW, (Int64)(10 * NSEC_PER_SEC))

dispatch_after(time, globalQueue) { () -> Void in

    println("10秒后执行")

} 

NSEC_PER_SEC表示的是秒数,它还提供了NSEC_PER_MSEC表示毫秒。

上面这句dispatch_after的真正含义是在10秒后把任务添加进队列中,并不是表示在10秒后执行,大部分情况该函数能达到我们的预期,只有在对时间要求非常精准的情况下才可能会出现问题。

获取一个dispatch_time_t类型的值可以通过两种方式来获取,以上是第一种方式,即通过dispatch_time函数,另一种是通过dispatch_walltime函数来获取,dispatch_walltime需要使用一个timespec的结构体来得到dispatch_time_t。通常dispatch_time用于计算相对时间,dispatch_walltime用于计算绝对时间,我写了一个把NSDate转成dispatch_time_t的Swift方法: 

func getDispatchTimeByDate(date: NSDate) -> dispatch_time_t {

    let interval = date.timeIntervalSince1970

    var second = 0.0

    let subsecond = modf(interval, &second)

    var time = timespec(tv_sec: __darwin_time_t(second), tv_nsec: (Int)(subsecond * (Double)(NSEC_PER_SEC)))

    return dispatch_walltime(&time, 0)

} 

这个方法接收一个NSDate对象,然后把NSDate转成dispatch_walltime需要的timespec结构体,最后再把dispatch_time_t返回,同样是在10秒后执行,之前的代码在调用部分需要修改成: 

var time = getDispatchTimeByDate(NSDate(timeIntervalSinceNow: 10))

dispatch_after(time, globalQueue) { () -> Void in

    println("10秒后执行")

}

这就是通过绝对时间来使用dispatch_after的例子。


dispatch_group

可能经常会有这样一种情况:我们现在有3个Block要执行,我们不在乎它们执行的顺序,我们只希望在这3个Block执行完之后再执行某个操作。这个时候就需要使用dispatch_group了:

let globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT0)

let group = dispatch_group_create()


dispatch_group_async(group, globalQueue) { () -> Void in

    println("1")

}

dispatch_group_async(group, globalQueue) { () -> Void in

    println("2")

}

dispatch_group_async(group, globalQueue) { () -> Void in

    println("3")

}

dispatch_group_notify(group, globalQueue) { () -> Void in

    println("completed")

}

输出的顺序与添加进队列的顺序无关,因为队列是Concurrent Dispatch Queue,但“completed”的输出一定是在最后的:
  1. 312  
  2. completed  
除了使用dispatch_group_notify函数可以得到最后执行完的通知外,还可以使用

let globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT0)

let group = dispatch_group_create()


dispatch_group_async(group, globalQueue) { () -> Void in

    println("1")

}

dispatch_group_async(group, globalQueue) { () -> Void in

    println("2")

}

dispatch_group_async(group, globalQueue) { () -> Void in

    println("3")

}

//使用dispatch_group_wait函数

dispatch_group_wait(group, DISPATCH_TIME_FOREVER)

println("completed")

需要注意的是,dispatch_group_wait实际上会使当前的线程处于等待的状态,也就是说如果是在主线程执行dispatch_group_wait,在上面的Block执行完之前,主线程会处于卡死的状态。可以注意到dispatch_group_wait的第二个参数是指定超时的时间,如果指定为DISPATCH_TIME_FOREVER(如上面这个例子)则表示会永久等待,直到上面的Block全部执行完,除此之外,还可以指定为具体的等待时间,根据dispatch_group_wait的返回值来判断是上面block执行完了还是等待超时了。
最后,同之前创建dispatch_queue一样,如果是在OS X 10.8或iOS 6以及之后版本中使用,Dispatch Group将会由ARC自动管理,如果是在此之前的版本,需要自己手动释放。

dispatch_barrier_async

dispatch_barrier_async就如同它的名字一样,在队列执行的任务中增加“栅栏”,在增加“栅栏”之前已经开始执行的block将会继续执行,当dispatch_barrier_async开始执行的时候其他的block处于等待状态,dispatch_barrier_async的任务执行完后,其后的block才会执行。我们简单的写个例子,假设这个例子有读文件和写文件的部分:

func writeFile() {

    NSUserDefaults.standardUserDefaults().setInteger(7, forKey: "Integer_Key")

}


func readFile(){

    print(NSUserDefaults.standardUserDefaults().integerForKey("Integer_Key"))

} 

写文件只是在NSUserDefaults写入一个数字7,读只是将这个数字打印出来而已。我们要避免在写文件时候正好有线程来读取,就使用dispatch_barrier_async函数: 

NSUserDefaults.standardUserDefaults().setInteger(9, forKey: "Integer_Key")

let globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT0)

dispatch_async(globalQueue) {self.readFile()}

dispatch_async(globalQueue) {self.readFile()}

dispatch_async(globalQueue) {self.readFile()}

dispatch_async(globalQueue) {self.readFile()}

dispatch_barrier_async(globalQueue) {self.writeFile() ; self.readFile()}

dispatch_async(globalQueue) {self.readFile()}

dispatch_async(globalQueue) {self.readFile()}

dispatch_async(globalQueue) {self.readFile()} 

我们先将一个9初始化到NSUserDefaults的Integer_Key中,然后在中间执行dispatch_barrier_async函数,由于这个队列是一个Concurrent Dispatch Queue,能同时并发多少线程是由系统决定的,如果添加dispatch_barrier_async的时候,其他的block(包括上面4个block)还没有开始执行,那么会先执行dispatch_barrier_async里的任务,其他block全部处于等待状态。如果添加dispatch_barrier_async的时候,已经有block在执行了,那么dispatch_barrier_async会等这些block执行完后再执行。


dispatch_apply

dispatch_apply会将一个指定的block执行指定的次数。如果要对某个数组中的所有元素执行同样的block的时候,这个函数就显得很有用了,用法很简单,指定执行的次数以及Dispatch Queue,在block回调中会带一个索引,然后就可以根据这个索引来判断当前是对哪个元素进行操作: 

let globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT0)

dispatch_apply(10, globalQueue) { (index) -> Void in

    print(index)

}

print("completed") 

由于是Concurrent Dispatch Queue,不能保证哪个索引的元素是先执行的,但是“completed”一定是在最后打印,因为dispatch_apply函数是同步的,执行过程中会使线程在此处等待,所以一般的,我们应该在一个异步线程里使用dispatch_apply函数:

let globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT0)

dispatch_async(globalQueue, { () -> Void in

    dispatch_apply(10, globalQueue) { (index) -> Void in

        print(index)

    }

    print("completed")

})

print("dispatch_apply之前") 


dispatch_suspend / dispatch_resume

某些情况下,我们可能会想让Dispatch Queue暂时停止一下,然后在某个时刻恢复处理,这时就可以使用dispatch_suspend以及dispatch_resume函数: 

//暂停

dispatch_suspend(globalQueue)

//恢复

dispatch_resume(globalQueue)

暂停时,如果已经有block正在执行,那么不会对该block的执行产生影响。dispatch_suspend只会对还未开始执行的block产生影响。

Dispatch Semaphore

信号量在多线程开发中被广泛使用,当一个线程在进入一段关键代码之前,线程必须获取一个信号量,一旦该关键代码段完成了,那么该线程必须释放信号量。其它想进入该关键代码段的线程必须等待前面的线程释放信号量。
信号量的具体做法是:当信号计数大于0时,每条进来的线程使计数减1,直到变为0,变为0后其他的线程将进不来,处于等待状态;执行完任务的线程释放信号,使计数加1,如此循环下去。
下面这个例子中使用了10条线程,但是同时只执行一条,其他的线程处于等待状态:

let globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT0)

let semaphore =  dispatch_semaphore_create(1)

for i in 0 ... 9 {

    dispatch_async(globalQueue, { () -> Void in

        dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER)

        let time = dispatch_time(DISPATCH_TIME_NOW, (Int64)(2 * NSEC_PER_SEC))

        dispatch_after(time, globalQueue) { () -> Void in

            print("2秒后执行")

            dispatch_semaphore_signal(semaphore)

        }

    })

}

取得信号量的线程在2秒后释放了信息量,相当于是每2秒执行一次。
通过上面的例子可以看到,在GCD中,用dispatch_semaphore_create函数能初始化一个信号量,同时需要指定信号量的初始值;使用dispatch_semaphore_wait函数分配信号量并使计数减1,为0时处于等待状态;使用dispatch_semaphore_signal函数释放信号量,并使计数加1。
另外dispatch_semaphore_wait同样也支持超时,只需要给其第二个参数指定超时的时候即可,同Dispatch Group的dispatch_group_wait函数类似,可以通过返回值来判断。
这个函数也需要注意,如果是在OS X 10.8或iOS 6以及之后版本中使用,Dispatch Semaphore将会由ARC自动管理,如果是在此之前的版本,需要自己手动释放。


dispatch_once

dispatch_once函数通常用在单例模式上,它可以保证在程序运行期间某段代码只执行一次,如果我们要通过dispatch_once创建一个单例类,在Swift可以这样:

class SingletonObject {

    class var sharedInstance : SingletonObject {

        struct Static {

            static var onceToken : dispatch_once_t = 0

            static var instance : SingletonObject? = nil

        }

        dispatch_once(&Static.onceToken) {

            Static.instance = SingletonObject()

        }

        return Static.instance!

    }

}

这样就能通过GCD的安全机制保证这段代码只执行一次。
Swift GCD
2018-03-19 14:17:00 weixin_33979745 阅读数 3

一、DispatchQueue

DispatchQueue 分为串行和并发,它的完整初始化方法为:

init(label: String, qos: DispatchQoS = default, attributes: DispatchQueue.Attributes = default, autoreleaseFrequency: DispatchQueue.AutoreleaseFrequency = default, target: DispatchQueue? = default)

可见,这些参数中,除了label,其它都有默认值(label表示该队列的标签,建议传值为反向域名字符串,如:com.onevcat.Kingfisher.Animator.preloadQueue)。

当除label外的参数都使用默认值时,初始化方法返回的便是串行队列。如果需要返回并发队列,参数attributes传值为.concurrent即可。DispatchQueue.Attributes 是一个结构体类型,该结构体提供了两个静态变量:concurrentinitiallyInactive(注意,没有代表串行队列的静态变量)。如果attributes参数传值为initiallyInactive, 任务不会自动执行,而是需要开发者手动调用activate()触发。但是代码依然是串行进行的,如果想要手动触发、并行执行任务,可以指定attributes参数接受一个数组: [.concurrent, .initiallyInactive]

参数qos代表队列执行的优先级,有六种优先级可供选择:

unspecified
background
default
utility
userInteractive
userInitiated

优先级从高到低依次为userInteractive>userInitiated>utility>background, 而default与unspecified介于userInteractive与background之间,具体有系统决定。

DispatchQueue.AutoreleaseFrequency有三种属性值.inherit.workItem.never
.inherit:不确定,之前默认的行为也是现在的默认值
.workItem:为每个执行的任务创建自动释放池,项目完成时清理临时对象
.never:GCD不为您管理自动释放池

参数target 用于指定即将创建的队列与队列target优先级相同。也可通过setTarget(queue: DispatchQueue?)函数指定与queue相同的优先级。

除了开发者自己创建队列,还可以通过DispatchQueue.main获取主队列(主队列也属于串行队列)、DispatchQueue.global(qos: DispatchQoS.QoSClass) 获取全局并发队列。

创建好了队列,通过sync { /*任务*/ }async { /*任务*/ } 将任务追加到队列中。串行队列或并发队列与sync或async组合总结:

串行队列 + sync : 队列中的任务在当前线程中依次执行,后面追加的任务会等到前面追加的任务执行完了才开始执行,不开新线程。当前线程取任务执行的队列不能与该串行队列相同,否则会发生线程死锁。
串行队列(非主队列) + async : 队列中的任务在新线程中依次执行。
主队列 + async : 将任务追加到主队列,当主队列中的其他任务执行完之后才会执行,并且在在主线程中执行。
并发队列 + sync : 队列中的任务在当前线程中依次执行。
并发队列 + async : 队列中的任务在新线程中并发执行。

不管哪种组合,队列中的任务出列的方式都是FIFO。

有时候希望追加到queue中的任务暂不执行,等待某一时刻执行,这时候可使用队列的suspend()函数和resume()函数。suspend()函数使队列的暂停计数加1,resume()函数使队列的暂停计数减一。

需要注意:
1、suspend()和resume()需要成对出现,否则会crash。
2、suspend()和resume()函数只对自己创建的队列有效,对系统提供的全局队列无效。
3、suspend()和 resume()对队列中的还未执行的任务有效,对于正在执行的任务无效。

二、DispatchGroup

在追加到DispatchQueue中的多个处理全部结束后想执行结束处理,这个时候就可用到DispatchGroup。示例如下:

let group = DispatchGroup()
let queue = DispatchQueue.global()
queue.async(group: group) {
     print("任务一")
}
queue.async(group: group) {
      print("任务二")
}
queue.async(group: group) {
     print("任务三")
}
group.notify(queue: DispatchQueue.main) {
     print("完成任务一、二、三")
}
queue.async {
     print("任务四")
}

运行结果:

2291385-b17e76de85651555.png

其中,queue既可以是同一个队列,也可以是不同的队列,既可以是串行队列,也可以是并发队列。
另外,也可以使用group的 group.wait(timeout: DispatchTime)group.wait(wallTimeout: DispatchWallTime)函数。wait 函数的参数表示等待的时间,默认是 DispatchTime.distantFuture,表示永久等待。wait函数会阻塞当前线程,即当执行的时间到了等待的时长,才会执行后面的代码。wait函数返回值是枚举类型DispatchTimeoutResult,DispatchTimeoutResult有successtimeOut两个枚举值,分别表示在等待时长内,任务执行完成和未完成。
我们还可以通过group的enter()函数和leave()函数显式表明任务是否执行完成。代码如下:

let group = DispatchGroup()
let queue = DispatchQueue.global()
group.enter()
queue.async {
     print("任务一")
     group.leave()
}
group.enter()
queue.async {
     print("任务二")
     group.leave()
}
group.enter()
queue.async {
    print("任务三")
    group.leave()
}
group.notify(queue: DispatchQueue.main) {
    print("完成任务一、二、三")
}
queue.async {
    print("任务四")
}

运行结果:

2291385-c6dbc0d9282aaec7.png

enter()leave()必须配合使用,有几次enter就要有几次leave,否则group会一直存在。当所有enter的block都leave后,会执行dispatch_group_notify的block。

三、asyncAfter

该函数用于延时操作。代码如下:

DispatchQueue.main.asyncAfter(wallDeadline: DispatchWallTime.now()+3) {
    print("执行任务")
} 

DispatchQueue.main.asyncAfter(deadline: DispatchTime.now()+3) {
    print("执行任务")
}

注意, asyncAfter函数并不是在指定时间后执行处理,而是在指定时间后将任务追加到队列中。

asyncAfter函数的第一个参数可以是DispatchTime类型的值,也可以是DispatchWallTime类型的值。

DispatchTime 表示相对时间(相对设备启动的时间,当设备休眠时,计时也会暂停),精度为纳秒级。DispatchTime.now() 获取当前相对时间,DispatchTime.now()基于当前时间三秒后的时间,表达式中的3也可以使用DispatchTimeInterval.seconds(3)替换,或者用其他的时间单位:毫秒级DispatchTimeInterval.milliseconds(Int) 、微秒级DispatchTimeInterval.milliseconds(Int)、纳秒级DispatchTimeInterval.nanoseconds(Int)
DispatchWallTime 表示绝对时间(系统时间,设备休眠计时不暂停),精度是微秒。DispatchWallTime的用法和DispatchTime差不多。

四、DispatchWorkItem

DispatchWorkItem可以将任务封装成DispatchWorkItem对象。

let workItem = DispatchWorkItem.init {
      print("执行任务")
}

可以调用workItem的perform()函数执行任务,也可以将workItem追加到DispatchQueue或DispatchGroup中。以上所有传block的地方都可换成DispatchWorkItem对象。
DispatchQueue还可以使用notify函数观察workItem中的任务执行结束,以及通过cancel()函数取消任务。

另外,workItem也可以像DispatchGroup一样调用wait()函数等待任务完成。需要注意的是,追加workItem的队列或调用perform()所在的队列不能与调用workItem.wait()的队列是同一个队列,否则会出现线程死锁。

DispatchWorkItem的完整初始化方法:

init(qos: DispatchQoS, flags: DispatchWorkItemFlags, block: () -> Void)

DispatchQoS前面已经说过,不再赘述。DispatchWorkItemFlags类型的变量有六种:

static let assignCurrentContext: DispatchWorkItemFlags
static let barrier: DispatchWorkItemFlags
static let detached: DispatchWorkItemFlags
static let enforceQoS: DispatchWorkItemFlags
static let inheritQoS: DispatchWorkItemFlags
static let noQoS: DispatchWorkItemFlags

为了高效地读写数据库或文件,通常需要将读写处理追加到并发队列
中异步执行,为了使读写操作不会引发数据竞争的问题,写入操作不能与其他的写入操作以及包含读取任务的操作并发处理,这时便可设置flag的值为.barrier。代码如下:

let queue = DispatchQueue.init(label: "com.codansYC.queue", attributes: DispatchQueue.Attributes.concurrent)
queue.async {
     print("读数据1")
}
queue.async {
     print("读数据2")
}
let workItem = DispatchWorkItem.init(qos: DispatchQoS.default, flags: DispatchWorkItemFlags.barrier) {
     print("开始写数据------写数据完成")
}
queue.async(execute: workItem)
queue.async {
     print("读数据3")
}
queue.async {
     print("读数据4")
}

运行结果:

2291385-b549dd0baed27c51.png

注意,barrier只对自己创建的并发队列才有效,对系统提供的全局并发队列无效。

五、DispatchQueue.concurrentPerform

sync函数和Dispatch Group的关联API。
DispatchQueue.concurrentPerform 会按指定次数异步执行任务,并且会等待指定次数的任务全部执行完成,即会阻塞线程。建议在子线程中使用。

DispatchQueue.global().async {
     DispatchQueue.concurrentPerform(iterations: 5) { (i) in
         print("执行任务\(i+1)")
     }
     print("任务执行完成")
}

运行结果:
2291385-e1c5427b5480e840.png

六、DispatchSemaphore

信号量。用于控制访问资源的数量。比如系统有两个资源可以被利用,同时有三个线程要访问,只能允许两个线程访问,第三个会等待资源被释放后再访问。
信号量的初始化方法:DispatchSemaphore.init(value: Int),value表示允许访问资源的线程数量,当value为0时对访问资源的线程没有限制。
信号量配套使用wait()函数与signal()函数控制访问资源。
wait函数会阻塞当前线程直到信号量计数大于或等于1,当信号量大于或等于1时,将信号量计数-1, 然后执行后面的代码。signal()函数会将信号量计数+1。

信号量是GCD同步的一种方式。前面介绍过的DispatchWorkItemFlags.barrier是对queue中的任务进行批量同步处理,sync函数是对queue中的任务单个同步处理,而DispatchSemaphore是对queue中的某个任务中的某部分(某段代码)同步处理。此时将DispatchSemaphore.init(value: Int)中的参数value传入1。代码如下:

var arr = [Int]()
let semaphore = DispatchSemaphore.init(value: 1) // 创建信号量,控制同时访问资源的线程数为1
for i in 0...100 {
    DispatchQueue.global().async {
                
        /*
        其他并发操作
        */
                
        semaphore.wait() // 如果信号量计数>=1,将信号量计数减1;如果信号量计数<1,阻塞线程直到信号量计数>=1
        arr.append(i)
        semaphore.signal() // 信号量计加1
                
        /*
        其他并发操作
        */
     }
}

转载于:https://www.jianshu.com/p/f5bece2b77d8

swift GCD
2017-12-13 16:23:00 weixin_34186931 阅读数 39

队列的创建和获取:

    // 串行队列
    let serialQueue = DispatchQueue(label: "www.google.com")
    
    // 并行队列
    let concurrentQueue = DispatchQueue(label: "www.google.com1", attributes: .concurrent)
    
    // 主队列
    let mainQueue = DispatchQueue.main
    
    // 全局并发队列
    let globalQueue = DispatchQueue.global()
    
    // 指定队列优先级
    let con = DispatchQueue(label: "conQueue", qos: .userInitiated)
    // 串行队列、同步执行    ---->> 在当前线程顺序执行
    func serialQueueSync() {
        print("begin")
        for i in 0...8 {
            serialQueue.sync {
                print(i, Thread.current)
            }
        }
        print("end")
    }
    
    // 串行队列、异步执行   ---->> 在新线程顺序执行
    func serialQueueAsync() {
        print("begin")
        for i in 0...8 {
            serialQueue.async {
                print(i, Thread.current)
            }
        }
        print("end")
    }
    
    // 并行队列、同步执行    ---->> 在当前线程顺序执行
    func concurrentQueueSync() {
        print("begin")
        for i in 0...180 {
            concurrentQueue.sync {
                print(i, Thread.current)
            }
        }
        print("end")
    }
    
    // 并行队列、异步执行    ---->> 在新线程乱序执行
    func concurrentQueueAsync() {
        print("begin")
        for i in 0...8 {
            concurrentQueue.async {
                print(i)
            }
        }
        print("end")
    }
    // 延时执行
    func queueDelay() {
        
        print(Date())
        Thread.sleep(forTimeInterval: 2)
        print(Date())
        // 延迟执行
        mainQueue.asyncAfter(deadline: .now() + 2) {
            print(Date())
            print(Thread.current)
        }
        print(Date())
    }
    // 线程间通讯
    func communicateBetweenQueue() {
        
        globalQueue.async {
            let a = "a" // 假设为延时操作,比如下载图片
            print(Thread.current)
            self.mainQueue.async {
                print(a, Thread.current)
            }
        }
    }
    // 线程安全
    func threadSafty() {
        var names = ["li"]
        
        for i in 0...10 {
            concurrentQueue.async {
                // 加锁 同OC @synchronized(self) {}
                objc_sync_enter(self)
                names.append("\(i)")
                print(names)
                objc_sync_exit(self)
            }
        }
        // 或利用阻塞式的特点
        for x in 11...20 {
            concurrentQueue.async(flags: .barrier, execute: {
                names.append("\(x)")
                print(names)
            })
        }
    }
// 阻塞式队列
    func barrier() {
        
        print("begin")
        for i in 40...60 {
            concurrentQueue.async {
                if i == 40 {
                    Thread.sleep(forTimeInterval: 1)
                }
                print(i)
            }
        }
        concurrentQueue.async(flags: .barrier) {
            print("barrier1", Thread.current)
        }
        for i in 30...39 {
            concurrentQueue.async {
                print(i)
            }
        }
        concurrentQueue.async(flags: .barrier) {
            print("barrier2", Thread.current)
        }
        for i in 0...29 {
            concurrentQueue.async {
                print(i)
            }
        }
        print("end")
    }
    // 信号量
    func dispatchSemaphore() {

        let semaphore = DispatchSemaphore(value: 1)
        concurrentQueue.async {
            _ = semaphore.wait(timeout: .distantFuture)
            print(1, Date())
            sleep(1)
            semaphore.signal()
        }

        concurrentQueue.async {
            _ = semaphore.wait(timeout: .distantFuture)
            print(2, Date())
            sleep(2)
            semaphore.signal()
        }
        
        concurrentQueue.async {
            _ = semaphore.wait(timeout: .distantFuture)
            print(3, Date())
            sleep(3)
            semaphore.signal()
        }
        print("semaphore")
    }
    // 队列组
    func dispatchGroup() {
        
        // 队列组
        let group = DispatchGroup()
        
        group.enter()
        concurrentQueue.async {
            sleep(1)
            print(1)
            group.leave()
        }
        
        group.enter()
        concurrentQueue.async {
    //      sleep(2)
            print(2)
            group.leave()
        }
        
        group.notify(queue: mainQueue) {
            print(3)
        }
    }
  • 如何设计一个线程安全的字典或数组?

在编码过程中,对数组进行多线程写入,会引起崩溃:

var names = ["li"]
for i in 0...10 {
    concurrentQueue.async {
        names.append("\(i)")
        print(names)
    }
}

采用部分代码加锁可解决这一问题:

var names = ["li"]
for i in 0...10 {
    concurrentQueue.async {
        // 加锁 同OC @synchronized(self) {}
        objc_sync_enter(self)
        names.append("\(i)")
        print(names)
        objc_sync_exit(self)
    }
}

在实践操作中,对数组进行多线程的读取并不会引起崩溃

var names = ["li", "wang", "sun", "zhao", "qiao", "zhang", "gai", "tu", "yi", "fei", "huo"]
for a in 0...10 {
    concurrentQueue.async {
        print(a, names[a])
    }
}

如何设计一个线程安全的字典或数组

实验证明多线程的读取数组或字典是不会引起崩溃的

多线程的写入字典或数组会引起崩溃

  1. 读取时保证没有别的线程在写
  2. 写入时保证没有别的线程在写或读
    当满足上面两个要求时可实现线程安全的字典或数组
  • 利用阻塞式的特点,我们将所有的写入操作写入阻塞式操作中,即可满足条件

GCD(Swift)

阅读数 4

原文链接这篇文主要想总结下多线程在swift中的使用,先看下基本概念进程进程指在系统中能独立运行并作为资源分配的基本单位,它是由一组机器指令、数据和堆栈等组成的,是一个能独立运行的活动实体线程线程是进程的基本执行单元,一个进程(程序)的所有任务都在线程中执行。队列队列,又称为伫列(queue),是先进先出(FIFO,First-In-First-Out)的线性表。在...

博文 来自: weixin_34217773

GCD(Swift)

阅读数 2

原文链接这篇文主要想总结下多线程在swift中的使用,先看下基本概念进程进程指在系统中能独立运行并作为资源分配的基本单位,它是由一组机器指令、数据和堆栈等组成的,是一个能独立运行的活动实体线程线程是进程的基本执行单元,一个进程(程序)的所有任务都在线程中执行。队列队列,又称为伫列(queue),是先进先出(FIFO,Firs...

博文 来自: weixin_34115824

Swift GCD

阅读数 51

一、DispatchQueueDispatchQueue分为串行和并发,它的完整初始化方法为:init(label:String,qos:DispatchQoS=default,attributes:DispatchQueue.Attributes=default,autoreleaseFrequency:DispatchQue...

博文 来自: weixin_33788244

swift GCD

阅读数 3

2019独角兽企业重金招聘Python工程师标准>>>...

博文 来自: weixin_34198762

gcd(swift)

阅读数 223

转自:http://blog.csdn.net/u011723466/article/details/41899411?locationNum=2转自:http://blog.csdn.net/zhangao0086/article/details/38904923 Bannings的专栏GrandCentralDispatch(GCD)是异步执行任务的技术之一

博文 来自: zhanglizhi111
没有更多推荐了,返回首页