Related
I'm trying to improve the time it takes for a task to finish by leveraging multithreading/paralleling.
I'm reading CMSampleBuffers from a video track, manipulating them, then storing them back to an array for later use. Because each manipulation is "costly" in terms of RAM and CPU, I'm using DispatchSemaphore to limit the number of parallel tasks (5 in the example).
Basically, I'm trying to make the "system" process more than a single frame in a time, and not above 5 so the device won't crash due to memory issues. In the current implementation below it's for some reason doing it almost serialize and not in parallel.
Any help will be highly appreciated!
I tried taking reference from here: How to limit gcd queue buffer size for the implementation.
Code:
class MyService {
let semaphore = DispatchSemaphore(value: 5)
let processQueue = DispatchQueue(label: "custom.process", attributes: .concurrent)
func startReading() {
for sampleBuffer in sampleBuffers {
// signal wait
semaphore.wait()
// async queue
processQueue.async {
// run taks
self.process(buffer: sampleBuffer) { pixelBuffer in
// singal to semaphore
self.semaphore.signal()
}
}
}
}
func process(buffer: CMSampleBuffer, completion: #escaping (CVPixelBuffer) -> (Void)) {
// run on a background thread to avoid UI freeze
DispatchQueue.global(qos: .userInteractive).async {
// Do something
// Do something
// Do something
// Do something
completion(processedBuffer)
}
}
}
In Swift 2, I was able to create queue with the following code:
let concurrentQueue = dispatch_queue_create("com.swift3.imageQueue", DISPATCH_QUEUE_CONCURRENT)
But this doesn't compile in Swift 3.
What is the preferred way to write this in Swift 3?
Creating a concurrent queue
let concurrentQueue = DispatchQueue(label: "queuename", attributes: .concurrent)
concurrentQueue.sync {
}
Create a serial queue
let serialQueue = DispatchQueue(label: "queuename")
serialQueue.sync {
}
Get main queue asynchronously
DispatchQueue.main.async {
}
Get main queue synchronously
DispatchQueue.main.sync {
}
To get one of the background thread
DispatchQueue.global(qos: .background).async {
}
Xcode 8.2 beta 2:
To get one of the background thread
DispatchQueue.global(qos: .default).async {
}
DispatchQueue.global().async {
// qos' default value is ´DispatchQoS.QoSClass.default`
}
If you want to learn about using these queues .See this answer
Compiles under >=Swift 3. This example contains most of the syntax that we need.
QoS - new quality of service syntax
weak self - to disrupt retain cycles
if self is not available, do nothing
async global utility queue - for network query, does not wait for the result, it is a concurrent queue, the block (usually) does not wait when started. Exception for a concurrent queue could be, when its task limit has been previously reached, then the queue temporarily turns into a serial queue and waits until some previous task in that queue completes.
async main queue - for touching the UI, the block does not wait for the result, but waits for its slot at the start. The main queue is a serial queue.
Of course, you need to add some error checking to this...
DispatchQueue.global(qos: .utility).async { [weak self] () -> Void in
guard let strongSelf = self else { return }
strongSelf.flickrPhoto.loadLargeImage { loadedFlickrPhoto, error in
if error != nil {
print("error:\(error)")
} else {
DispatchQueue.main.async { () -> Void in
activityIndicator.removeFromSuperview()
strongSelf.imageView.image = strongSelf.flickrPhoto.largeImage
}
}
}
}
Compiled in XCode 8, Swift 3
https://github.com/rpthomas/Jedisware
#IBAction func tap(_ sender: AnyObject) {
let thisEmail = "emailaddress.com"
let thisPassword = "myPassword"
DispatchQueue.global(qos: .background).async {
// Validate user input
let result = self.validate(thisEmail, password: thisPassword)
// Go back to the main thread to update the UI
DispatchQueue.main.async {
if !result
{
self.displayFailureAlert()
}
}
}
}
Since the OP question has already been answered above I just want to add some speed considerations:
It makes a lot of difference what priority class you assign to your async function in DispatchQueue.global.
I don't recommend running tasks with the .background thread priority especially on the iPhone X where the task seems to be allocated on the low power cores.
Here is some real data from a computationally intensive function that reads from an XML file (with buffering) and performs data interpolation:
Device name / .background / .utility / .default / .userInitiated / .userInteractive
iPhone X: 18.7s / 6.3s / 1.8s / 1.8s / 1.8s
iPhone 7: 4.6s / 3.1s / 3.0s / 2.8s / 2.6s
iPhone 5s: 7.3s / 6.1s / 4.0s / 4.0s / 3.8s
Note that the data set is not the same for all devices. It's the biggest on the iPhone X and the smallest on the iPhone 5s.
Update for swift 5
Serial Queue
let serialQueue = DispatchQueue.init(label: "serialQueue")
serialQueue.async {
// code to execute
}
Concurrent Queue
let concurrentQueue = DispatchQueue.init(label: "concurrentQueue", qos: .background, attributes: .concurrent, autoreleaseFrequency: .inherit, target: nil)
concurrentQueue.async {
// code to execute
}
From Apple documentation:
Parameters
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.
qos
The quality-of-service level to associate with the queue. This value determines the priority at which the system schedules tasks for execution. For a list of possible values, see DispatchQoS.QoSClass.
attributes
The attributes to associate with the queue. Include the concurrent attribute to create a dispatch queue that executes tasks concurrently. If you omit that attribute, the dispatch queue executes tasks serially.
autoreleaseFrequency
The frequency with which to autorelease objects created by the blocks that the queue schedules. For a list of possible values, see DispatchQueue.AutoreleaseFrequency.
target
The target queue on which to execute blocks. Specify DISPATCH_TARGET_QUEUE_DEFAULT if you want the system to provide a queue that is appropriate for the current object.
I did this and this is especially important if you want to refresh your UI to show new data without user noticing like in UITableView or UIPickerView.
DispatchQueue.main.async
{
/*Write your thread code here*/
}
DispatchQueue.main.async {
self.collectionView?.reloadData() // Depends if you were populating a collection view or table view
}
OperationQueue.main.addOperation {
self.lblGenre.text = self.movGenre
}
//use Operation Queue if you need to populate the objects(labels, imageview, textview) on your viewcontroller
let concurrentQueue = dispatch_queue_create("com.swift3.imageQueue", DISPATCH_QUEUE_CONCURRENT) //Swift 2 version
let concurrentQueue = DispatchQueue(label:"com.swift3.imageQueue", attributes: .concurrent) //Swift 3 version
I re-worked your code in Xcode 8, Swift 3 and the changes are marked in contrast to your Swift 2 version.
Swift 3
you want call some closure in swift code then you want to change in storyboard ya any type off change belong to view your application will crash
but you want to use dispatch method your application will not crash
async method
DispatchQueue.main.async
{
//Write code here
}
sync method
DispatchQueue.main.sync
{
//Write code here
}
DispatchQueue.main.async(execute: {
// write code
})
Serial Queue :
let serial = DispatchQueue(label: "Queuename")
serial.sync {
//Code Here
}
Concurrent queue :
let concurrent = DispatchQueue(label: "Queuename", attributes: .concurrent)
concurrent.sync {
//Code Here
}
For Swift 3
DispatchQueue.main.async {
// Write your code here
}
let newQueue = DispatchQueue(label: "newname")
newQueue.sync {
// your code
}
it is now simply:
let serialQueue = DispatchQueue(label: "my serial queue")
the default is serial, to get concurrent, you use the optional attributes argument .concurrent
DispatchQueue.main.async(execute: {
// code
})
You can create dispatch queue using this code in swift 3.0
DispatchQueue.main.async
{
/*Write your code here*/
}
/* or */
let delayTime = DispatchTime.now() + Double(Int64(0.5 * Double(NSEC_PER_SEC))) / Double(NSEC_PER_SEC)
DispatchQueue.main.asyncAfter(deadline: delayTime)
{
/*Write your code here*/
}
I need to sync web database in my coredata, for which I perform service api calls. I am using Alamofire with Swift 3. There are 23 api calls, giving nearly 24k rows in different coredata entities.
My problem: These api calls blocks UI for a minute, which is a long time for a user to wait.
I tried using DispatchQueue and performing the task in background thread, though nothing worked. This is how I tried :
let dataQueue = DispatchQueue.init(label: "com.app.dataSyncQueue")
dataQueue.async {
DataSyncController().performStateSyncAPICall()
DataSyncController().performRegionSyncAPICall()
DataSyncController().performStateRegionSyncAPICall()
DataSyncController().performBuildingRegionSyncAPICall()
PriceSyncController().performBasicPriceSyncAPICall()
PriceSyncController().performHeightCostSyncAPICall()
// Apis which will be used in later screens are called in background
self.performSelector(inBackground: #selector(self.performBackgroundTask), with: nil)
}
An API call from DataSyncController:
func performStateSyncAPICall() -> Void {
DataSyncRequestManager.fetchStatesDataWithCompletionBlock {
success, response, error in
self.apiManager.didStatesApiComplete = true
}
}
DataSyncRequestManager Code:
static func fetchStatesDataWithCompletionBlock(block:#escaping requestCompletionBlock) {
if appDelegate.isNetworkAvailable {
Util.setAPIStatus(key: kStateApiStatus, with: kInProgress)
DataSyncingInterface().performStateSyncingWith(request:DataSyncRequest().createStateSyncingRequest() , withCompletionBlock: block)
} else {
//TODO: show network failure error
}
}
DataSyncingInterface Code:
func performStateSyncingWith(request:Request, withCompletionBlock block:#escaping requestCompletionBlock)
{
self.interfaceBlock = block
let apiurl = NetworkHttpClient.getBaseUrl() + request.urlPath!
Alamofire.request(apiurl, parameters: request.getParams(), encoding: URLEncoding.default).responseJSON { response in
guard response.result.isSuccess else {
block(false, "error", nil )
return
}
guard let responseValue = response.result.value else {
block (false, "error", nil)
return
}
block(true, responseValue, nil)
}
}
I know many similar questions have been already posted on Stackoverflow and mostly it is suggested to use GCD or Operation Queue, though trying DispatchQueues didn't work for me.
Am I doing something wrong?
How can I not block UI and perform the api calls simultaneously?
You can do this to run on a background thread:
DispatchQueue.global(qos: .background).async {
// Do any processing you want.
DispatchQueue.main.async {
// Go back to the main thread to update the UI.
}
}
DispatchQueue manages the execution of work items. Each work item submitted to a queue is processed on a pool of threads managed by the system.
I usually use NSOperationQueue with Alamofire, but the concepts are similar. When you set up an async queue, you allow work to be performed independently of the main (UI) thread, so that your app doesn't freeze (refuse user input). The work will still take however long it takes, but your program doesn't block while waiting to finish.
You really have only put one item into the queue.
You are adding to the queue only once, so all those "perform" calls wait for the previous one to finish. If it is safe to run them concurrently, you need to add each of them to the queue separately. There's more than one way to do this, but the bottom line is each time you call .async {} you are adding one item to the queue.
dataQueue.async {
DataSyncController().performStateSyncAPICall()
}
dataQueue.async {
DataSyncController(). performRegionSyncAPICall l()
}
In Swift 2, I was able to create queue with the following code:
let concurrentQueue = dispatch_queue_create("com.swift3.imageQueue", DISPATCH_QUEUE_CONCURRENT)
But this doesn't compile in Swift 3.
What is the preferred way to write this in Swift 3?
Creating a concurrent queue
let concurrentQueue = DispatchQueue(label: "queuename", attributes: .concurrent)
concurrentQueue.sync {
}
Create a serial queue
let serialQueue = DispatchQueue(label: "queuename")
serialQueue.sync {
}
Get main queue asynchronously
DispatchQueue.main.async {
}
Get main queue synchronously
DispatchQueue.main.sync {
}
To get one of the background thread
DispatchQueue.global(qos: .background).async {
}
Xcode 8.2 beta 2:
To get one of the background thread
DispatchQueue.global(qos: .default).async {
}
DispatchQueue.global().async {
// qos' default value is ´DispatchQoS.QoSClass.default`
}
If you want to learn about using these queues .See this answer
Compiles under >=Swift 3. This example contains most of the syntax that we need.
QoS - new quality of service syntax
weak self - to disrupt retain cycles
if self is not available, do nothing
async global utility queue - for network query, does not wait for the result, it is a concurrent queue, the block (usually) does not wait when started. Exception for a concurrent queue could be, when its task limit has been previously reached, then the queue temporarily turns into a serial queue and waits until some previous task in that queue completes.
async main queue - for touching the UI, the block does not wait for the result, but waits for its slot at the start. The main queue is a serial queue.
Of course, you need to add some error checking to this...
DispatchQueue.global(qos: .utility).async { [weak self] () -> Void in
guard let strongSelf = self else { return }
strongSelf.flickrPhoto.loadLargeImage { loadedFlickrPhoto, error in
if error != nil {
print("error:\(error)")
} else {
DispatchQueue.main.async { () -> Void in
activityIndicator.removeFromSuperview()
strongSelf.imageView.image = strongSelf.flickrPhoto.largeImage
}
}
}
}
Compiled in XCode 8, Swift 3
https://github.com/rpthomas/Jedisware
#IBAction func tap(_ sender: AnyObject) {
let thisEmail = "emailaddress.com"
let thisPassword = "myPassword"
DispatchQueue.global(qos: .background).async {
// Validate user input
let result = self.validate(thisEmail, password: thisPassword)
// Go back to the main thread to update the UI
DispatchQueue.main.async {
if !result
{
self.displayFailureAlert()
}
}
}
}
Since the OP question has already been answered above I just want to add some speed considerations:
It makes a lot of difference what priority class you assign to your async function in DispatchQueue.global.
I don't recommend running tasks with the .background thread priority especially on the iPhone X where the task seems to be allocated on the low power cores.
Here is some real data from a computationally intensive function that reads from an XML file (with buffering) and performs data interpolation:
Device name / .background / .utility / .default / .userInitiated / .userInteractive
iPhone X: 18.7s / 6.3s / 1.8s / 1.8s / 1.8s
iPhone 7: 4.6s / 3.1s / 3.0s / 2.8s / 2.6s
iPhone 5s: 7.3s / 6.1s / 4.0s / 4.0s / 3.8s
Note that the data set is not the same for all devices. It's the biggest on the iPhone X and the smallest on the iPhone 5s.
Update for swift 5
Serial Queue
let serialQueue = DispatchQueue.init(label: "serialQueue")
serialQueue.async {
// code to execute
}
Concurrent Queue
let concurrentQueue = DispatchQueue.init(label: "concurrentQueue", qos: .background, attributes: .concurrent, autoreleaseFrequency: .inherit, target: nil)
concurrentQueue.async {
// code to execute
}
From Apple documentation:
Parameters
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.
qos
The quality-of-service level to associate with the queue. This value determines the priority at which the system schedules tasks for execution. For a list of possible values, see DispatchQoS.QoSClass.
attributes
The attributes to associate with the queue. Include the concurrent attribute to create a dispatch queue that executes tasks concurrently. If you omit that attribute, the dispatch queue executes tasks serially.
autoreleaseFrequency
The frequency with which to autorelease objects created by the blocks that the queue schedules. For a list of possible values, see DispatchQueue.AutoreleaseFrequency.
target
The target queue on which to execute blocks. Specify DISPATCH_TARGET_QUEUE_DEFAULT if you want the system to provide a queue that is appropriate for the current object.
I did this and this is especially important if you want to refresh your UI to show new data without user noticing like in UITableView or UIPickerView.
DispatchQueue.main.async
{
/*Write your thread code here*/
}
DispatchQueue.main.async {
self.collectionView?.reloadData() // Depends if you were populating a collection view or table view
}
OperationQueue.main.addOperation {
self.lblGenre.text = self.movGenre
}
//use Operation Queue if you need to populate the objects(labels, imageview, textview) on your viewcontroller
let concurrentQueue = dispatch_queue_create("com.swift3.imageQueue", DISPATCH_QUEUE_CONCURRENT) //Swift 2 version
let concurrentQueue = DispatchQueue(label:"com.swift3.imageQueue", attributes: .concurrent) //Swift 3 version
I re-worked your code in Xcode 8, Swift 3 and the changes are marked in contrast to your Swift 2 version.
Swift 3
you want call some closure in swift code then you want to change in storyboard ya any type off change belong to view your application will crash
but you want to use dispatch method your application will not crash
async method
DispatchQueue.main.async
{
//Write code here
}
sync method
DispatchQueue.main.sync
{
//Write code here
}
DispatchQueue.main.async(execute: {
// write code
})
Serial Queue :
let serial = DispatchQueue(label: "Queuename")
serial.sync {
//Code Here
}
Concurrent queue :
let concurrent = DispatchQueue(label: "Queuename", attributes: .concurrent)
concurrent.sync {
//Code Here
}
For Swift 3
DispatchQueue.main.async {
// Write your code here
}
let newQueue = DispatchQueue(label: "newname")
newQueue.sync {
// your code
}
it is now simply:
let serialQueue = DispatchQueue(label: "my serial queue")
the default is serial, to get concurrent, you use the optional attributes argument .concurrent
DispatchQueue.main.async(execute: {
// code
})
You can create dispatch queue using this code in swift 3.0
DispatchQueue.main.async
{
/*Write your code here*/
}
/* or */
let delayTime = DispatchTime.now() + Double(Int64(0.5 * Double(NSEC_PER_SEC))) / Double(NSEC_PER_SEC)
DispatchQueue.main.asyncAfter(deadline: delayTime)
{
/*Write your code here*/
}
ok I am updating this question but left the old one there.
So I have an array that stores the data for different views in a uipageviewcontroller. I need to grab image data in the background. I don't understand how to code this though within an asynchronous task.
Heres the code for the task:
let queue = NSOperationQueue()
queue.addOperationWithBlock() {
// do something in the background
println("background")
self.cards[newIndex].loadImag()
var cardimages = self.cards[newIndex].images
NSOperationQueue.mainQueue().addOperationWithBlock() {
// when done, update your UI and/or model on the main queue
println("update ui")
self.cards[newIndex].images = cardimages
}
}
this is what the .loadImag() function looks like:
func loadImag(){
println("images= \(self.images)")
if self.
location_id != nil && (self.images == nil) {
println("before api call loc_id= \(self.location_id)")
ApiWrapper.getPictures(self.location_id!, completionHandler: self.imagesCallback)
}
}
}
and this is self.imagesCallback code:
private func imagesCallback(cardImagesArray: [CardImage]){
println("images callback id= \(self.location_id)")
self.images = cardImagesArray
}
problem is I am not sure how to put this code inside of the operation cue since the function must have a callback. How can I get the operation queue working so that it updates the self.card array in the uipageviewcontroller?
OLD QUESTION_________________:
So I have this line of code I need to run concurrently in a different thread than the main thread. When I add it to the main queue like so:
var queue = dispatch_get_main_queue()
dispatch_async(queue, {
self.cards[newIndex].loadImage()
})
doing this it works fine but doesn't seem to run concurrently. When I change the queue to concurrent like this:
dispatch_async(DISPATCH_QUEUE_CONCURRENT, {
self.cards[newIndex].loadImage()
})
The app crashes saying "EXC_BAD_ACCESS". What am I doing wrong here? Also when I run the self.cards[newIndex].loadImage() function in a different concurrent thread will this update the values in the main thread?
you shouldn't use GCD unless you want to explicitly use functionality which is only available on GCD. For your case it is more beneficial (and cleaner in code) to use NSOperationQueue. NSOperationQueue uses GCD in the background and is more secure (less ways to mess up)
let queue = NSOperationQueue()
queue.addOperationWithBlock() {
// do something in the background
NSOperationQueue.mainQueue().addOperationWithBlock() {
// when done, update your UI and/or model on the main queue
}
}
You can also read through the Apple Concurrency Programming Guide
The guide is using examples with Objective-C but API is basically the same for Swift.
It also might look strange but with the addOperationWithBlock() I used something called "trailing closure" you can read here about it
Can you paste the whole code so we can see what are you doing?
Below is very basic code snippet. This is basically how concurrency works in Swift.
let qos = Int(QOS_CLASS_USER_INITIATED.value)
dispatch_async(dispatch_get_global_queue(qos, 0), { () -> Void in
// Put your code here to work in the background
dispatch_async(dispatch_get_main_queue(), { () -> Void in
// Put your code here when the process is done and hand over to the main thread.
// Ex. self.cards[newIndex].loadImage()
})
})
You need to use dispatch_get_global_queue . Try something like:
let queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)
dispatch_async(queue, {self.cards[newIndex].loadImage()})
dispatch_get_main_queue(), as you were trying, runs on the UI/main thread, which is why you saw the behavior you did.
To answer the second part of your question, If loadImage() is modifying the UI, you don't want to do that from a background thread. It must be done from the main/UI thread. A typical idiom would be, from the main thread do:
let queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)
dispatch_async(queue, {
<code to load/prepare images>
dispatch_async(dispatch_get_main_queue(), {
<code to update UI elements>
})
})