What are the benefits of using async-await in the background thread? - swift

I'm trying to understand the proper application of async-await in Swift. Let's say an async method, a non-IO method that doesn't make external calls, is called from a background thread to execute its process, such as some heavy image processing method.
func processImage(image: UIImage) async -> UIImage {
///
}
Task {
let result = await processImage(image: image)
}
What is happening when the code is paused and is waiting for the result? Since it's not making an external call, the process must be done somewhere from within the pool of threads. And since it's not being done in the very thread the method is called from, it must be being executed on another thread. Is a subtask created to execute the process? From what I understand, Task is a unit of concurrency and a single task contains no concurrency (except for async let), so this is a bit confusing to me. Is a task concurrent or not concurrent?
I understand that if this method is called from the main thread, the non-blocking aspect of the async method frees up the thread for the UI elements to run, thereby providing a seamless visual experience. But, what is the benefit of calling an async method from a background thread? I'm not referring to the syntactic sugar of being able to return the results or throw errors. Are there any benefits to the non-blocking aspect as opposed to using a synchronous method if the method is a non-IO method called from the background? In other words, what is it not blocking? If it's a parallel process, it's utilizing more resources to process multiple things efficiently, but I'm not sure how a concurrent process in this case is beneficial.

You need to stop thinking in terms of threads if you want to use async/await. It is useful, to some extent and for obvious reasons, to keep using phrases like "on the main thread" and "on a background thread", but these are almost metaphors.
You just need to accept that, no matter what "thread" something runs on, await has the magical power to say "hold my place" and to permit the computer to walk away and do something else entirely until the thing we're waiting for comes back to us. Nothing blocks, nothing spins. That is, indeed, a large part of the point of async/await.
(If you want to understand how it works under the hood, you need to find out what a "continuation" is. But in general it's really not worth worrying about; it's just a matter of getting your internal belief system in order.)
The overall advantage of async/await, however, is syntactic, not mechanical. Perhaps you could have done in effect everything you would do via async/await by using some other mechanism (Combine, DispatchQueue, Operation, whatever). But experience has shown that, especially in the case of DispatchQueue, beginners (and not-so-beginners) have great difficulty reasoning about the order in which lines of code are executed when code is asynchronous. With async/await, that problem goes away: code is executed in the order in which it appears, as if it were not asynchronous at all.
And not just programmers; the compiler can't reason about the correctness of your DispatchQueue code. It can't help catch you in your blunders (and I'm sure you've made a few in your time; I certainly have). But async/await is not like that; just the opposite: the compiler can reason about your code and can help keep everything neat, safe, and correct.
As for the actual example that you pose, the correct implementation is to define an actor whose job it is to perform the time-consuming task. This, by definition, will not be the main actor; since you defined it, it will be what we may call a background actor; its methods will be called off the main thread, automatically, and everything else will follow thanks to the brilliance of the compiler.
Here is an example (from my book), doing just the sort of thing you ask about — a time-consuming calculation. This is a view which, when you call its public drawThatPuppy method, calculates a crude image of the Mandelbrot set off the main thread and then portrays that image within itself. The key thing to notice, for your purposes, is that in the lines
self.bitmapContext = await self.calc.drawThatPuppy(center: center, bounds: bounds)
self.setNeedsDisplay()
the phrases self.bitmapContext = and self.setNeedsDisplay are executed on the main thread, but the call to self.calc.drawThatPuppy is executed on a background thread, because calc is an actor. Yet the main thread is not blocked while self.calc.drawThatPuppy is executing; on the contrary, other main thread code is free to run during that time. It's a miracle!
// Mandelbrot drawing code based on https://github.com/ddeville/Mandelbrot-set-on-iPhone
import UIKit
extension CGRect {
init(_ x:CGFloat, _ y:CGFloat, _ w:CGFloat, _ h:CGFloat) {
self.init(x:x, y:y, width:w, height:h)
}
}
/// View that displays mandelbrot set
class MyMandelbrotView : UIView {
var bitmapContext: CGContext!
var odd = false
// the actor declaration puts us on the background thread
private actor MyMandelbrotCalculator {
private let MANDELBROT_STEPS = 200
func drawThatPuppy(center:CGPoint, bounds:CGRect) -> CGContext {
let bitmap = self.makeBitmapContext(size: bounds.size)
self.draw(center: center, bounds: bounds, zoom: 1, context: bitmap)
return bitmap
}
private func makeBitmapContext(size:CGSize) -> CGContext {
var bitmapBytesPerRow = Int(size.width * 4)
bitmapBytesPerRow += (16 - (bitmapBytesPerRow % 16)) % 16
let colorSpace = CGColorSpaceCreateDeviceRGB()
let prem = CGImageAlphaInfo.premultipliedLast.rawValue
let context = CGContext(data: nil, width: Int(size.width), height: Int(size.height), bitsPerComponent: 8, bytesPerRow: bitmapBytesPerRow, space: colorSpace, bitmapInfo: prem)
return context!
}
private func draw(center:CGPoint, bounds:CGRect, zoom:CGFloat, context:CGContext) {
func isInMandelbrotSet(_ re:Float, _ im:Float) -> Bool {
var fl = true
var (x, y, nx, ny) : (Float, Float, Float, Float) = (0,0,0,0)
for _ in 0 ..< MANDELBROT_STEPS {
nx = x*x - y*y + re
ny = 2*x*y + im
if nx*nx + ny*ny > 4 {
fl = false
break
}
x = nx
y = ny
}
return fl
}
context.setAllowsAntialiasing(false)
context.setFillColor(red: 0, green: 0, blue: 0, alpha: 1)
var re : CGFloat
var im : CGFloat
let maxi = Int(bounds.size.width)
let maxj = Int(bounds.size.height)
for i in 0 ..< maxi {
for j in 0 ..< maxj {
re = (CGFloat(i) - 1.33 * center.x) / 160
im = (CGFloat(j) - 1.0 * center.y) / 160
re /= zoom
im /= zoom
if (isInMandelbrotSet(Float(re), Float(im))) {
context.fill (CGRect(CGFloat(i), CGFloat(j), 1.0, 1.0))
}
}
}
}
}
private let calc = MyMandelbrotCalculator()
// jumping-off point: draw the Mandelbrot set
func drawThatPuppy() async {
let bounds = self.bounds
let center = CGPoint(x: bounds.midX, y: bounds.midY)
self.bitmapContext =
await self.calc.drawThatPuppy(center: center, bounds: bounds)
self.setNeedsDisplay()
}
// turn pixels of self.bitmapContext into CGImage, draw into ourselves
override func draw(_ rect: CGRect) {
if self.bitmapContext != nil {
let context = UIGraphicsGetCurrentContext()!
context.setFillColor(self.odd ? UIColor.red.cgColor : UIColor.green.cgColor)
self.odd.toggle()
context.fill(self.bounds)
let im = self.bitmapContext.makeImage()
context.draw(im!, in: self.bounds)
}
}
}

The Swift Programming Language: Concurrency defines an asynchronous function as “a special kind of function or method that can be suspended while it’s partway through execution.”
So, this async designation on a function is designed for truly asynchronous routines, where the function will suspend/await the execution while the asynchronous process is underway. A typical example of this is the fetching of data with URLSession.
But this computationally intensive image processing is not an asynchronous task. It is inherently synchronous. So, it does not make sense to mark it as async. Furthermore, Task {…} is probably not the right pattern, either, as that creates a “new top-level task on behalf of the current actor”. But you probably do not want that slow, synchronous process running on the current actor (certainly, if that is the main actor). You may want a detached task. Or put it on its own actor.
The below code snippet illustrates how truly asynchronous methods (like the network request to fetch the data, fetchImage) differ from slow, synchronous methods (the processing of the image in processImage):
func processedImage(from url: URL) async throws -> UIImage {
// fetch from network (calling `async` function)
let image = try await fetchImage(from: url)
// process synchronously, but do so off the current actor, so
// we don’t block this actor
return try await Task.detached {
await self.processImage(image)
}.value
}
// asynchronous method to fetch image
func fetchImage(from url: URL) async throws -> UIImage {
let (data, response) = try await URLSession.shared.data(from: url)
guard let image = UIImage(data: data) else { throw ImageError.notImage }
return image
}
// slow, synchronous method to process image
func processImage(_ image: UIImage) -> UIImage {
…
}
enum ImageError: Error {
case notImage
}
For more information, see WWDC 2021 video Meet async/await in Swift. For insights about what await (i.e., a suspension point) really means within the broader threading model, Swift concurrency: Behind the scenes might be an interesting watch.

Related

asyncDetached falling back into main thread after MainActor call

I'm trying out the new async/await stuff. My goal here is to run the test() method in the background, so I use Task.detached; but during test() I need to make a call on the main thread, so I'm using MainActor.
(I realize that this may look convoluted in isolation, but it's pared down from a much better real-world case.)
Okay, so test code looks like this (in a view controller):
override func viewDidLoad() {
super.viewDidLoad()
Task.detached(priority: .userInitiated) {
await self.test()
}
}
#MainActor func getBounds() async -> CGRect {
let bounds = self.view.bounds
return bounds
}
func test() async {
print("test 1", Thread.isMainThread) // false
let bounds = await self.getBounds()
print("test 2", Thread.isMainThread) // true
}
The first print says I'm not on the main thread. That's what I expect.
But the second print says I am on the main thread. That isn't what I expect.
It feels as if I've mysteriously fallen back into the main thread just because I called a MainActor function. I thought I would be waiting for the main thread and then resuming in the background thread I was already on.
Is this a bug, or are my expectations mistaken? If the latter, how do I step out to the main thread during await but then come back to the thread I was on? I thought this was exactly what async/await would make easy...?
(I can "solve" the problem, in a way, by calling Task.detached again after the call to getBounds; but at that point my code looks so much like nested GCD that I have to wonder why I'm using async/await at all.)
Maybe I'm being premature but I went ahead and filed this as a bug: https://bugs.swift.org/browse/SR-14756.
More notes:
I can solve the problem by replacing
let bounds = await self.getBounds()
with
async let bounds = self.getBounds()
let thebounds = await bounds
But that seems unnecessarily elaborate, and doesn't convince me that the original phenomenon is not a bug.
I can also solve the problem by using actors, and this is starting to look like the best approach. But again, that doesn't persuade me that the phenomenon I'm noting here is not a bug.
I'm more and more convinced that this is a bug. I just encountered (and reported) the following:
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
async {
print("howdy")
await doSomeNetworking()
}
}
func doSomeNetworking() async {
print(Thread.isMainThread)
}
This prints howdy and then the second print prints true. But if we comment out the first print, the remaining (second) print prints false!
How can merely adding or removing a print statement change what thread we're on? Surely that's not intended.
As I understand it, given that this is all very new, there is no guarantee that asyncDetached must schedule off the main thread.
In the Swift Concurrency: Behind the Scenes session, it's discussed that the scheduler will try to keep things on the same thread to avoid context switches. Given that though, I don't know how you would specifically avoid the main thread, but maybe we're not supposed to care as long as the task makes progress and never blocks.
I found the timestamp (23:18) that explains that there is no guarantee that the same thread will pick up a continuation after an await. https://developer.apple.com/videos/play/wwdc2021/10254/?time=1398
Even if you do the testing to figure out the exact threading behavior of awaiting on #MainActor functions, you should not rely on it. As in #fullsailor's answer, the language explicitly does not guarantee that work will be resumed on the same thread after an await, so this behavior could change in any OS update. In the future, you may be able to request a specific thread by using a custom executor, but this is not currently in the language. See https://github.com/rjmccall/swift-evolution/blob/custom-executors/proposals/0000-custom-executors.md for more details.
Further, it hopefully should not cause any problems that you are running on the main thread. See https://developer.apple.com/videos/play/wwdc2021/10254/?time=2074 for details about how scheduling works. You should not be afraid of blocking the main thread by calling a #MainActor function and then doing expensive work afterwards: if there is more important UI work available, this will be scheduled before your work, or your work will be run on another thread. If you are particularly worried, you can use Task.yield() before your expensive work to give Swift another opportunity to move your work off the main thread. See Voluntary Suspension here for more details on Task.yield().
In your example, it is likely that Swift decided that it is not worth the performance hit of context switching back from the main thread since it was already there, but if the main thread were more saturated, you might experience different behavior.
Edit:
The behavior you're seeing with async let is because this spawns a child task which runs concurrently with the work you are doing. Thus, since that child is running on the main thread, your other code isn't. See https://forums.swift.org/t/concurrency-structured-concurrency/41622 for more details on child tasks.
The following formulation works, and solves the entire problem very elegantly, though I'm a little reluctant to post it because I don't really understand how it works:
override func viewDidLoad() {
super.viewDidLoad()
Task {
await self.test2()
}
}
nonisolated func test2() async {
print("test 1", Thread.isMainThread) // false
let bounds = await self.view.bounds // access on main thread!
print("test 2", bounds, Thread.isMainThread) // false
}
I've tested the await self.view.bounds call up the wazoo, and both the view access and the bounds access are on the main thread. The nonisolated designation here is essential to ensuring this. The need for this and the concomitant need for await are very surprising to me, but it all seems to have to do with the nature of actors and the fact that a UIViewController is a MainActor.
A brief note that since the question was asked, the UIKit classes got marked with #MainActor, so the code in discussion would print true on both occasions. But the problem can still be reproducible with a "regular" class.
Now, getting back to the dicussed behaviour, it's expected, and as others have said its also logical:
premature optimization is the root of all evil, thread context switches are expensive, so the runtime doesn't easily jump at doing them
the test function is not entirely in a concurrent context, because the code hops between the MainActor and your class, thus, the Swift runtime doesn't know that it has to get back to the cooperative thread pool.
If you convert your class to an actor, you'll see the behaviour you expect. Here's a tweaked actor based on the code from the question:
actor ThreadTester {
func viewDidLoad() {
Task.detached(priority: .userInitiated) {
await self.test()
}
}
#MainActor func getBounds() async -> CGRect {
.zero
}
func test() async {
print("test 1", Thread.isMainThread) // false
let bounds = await self.getBounds()
print("test 2", Thread.isMainThread) // true
}
}
Task {
await ThreadTester().viewDidLoad()
}
You can toggle between actor and class, leaving the other code untouched, and you'll consistently see the two behaviours.
Swift's structured concurrency works best if all entities involved in concurrent operations are already part of the structured concurrency family, as in this case the compiler has all the necessary information to make informed decisions.
Checking on which thread task is running is unreliable since swift concurrency may use same thread across multiple tasks to avoid context switches. You have to use actor isolation to make sure your tasks aren't executed on actor (this applies to any actor along with MainActor).
First of all, actors in swift are reentrant. This means whenever you are making an async call actor suspends current task until the method returns and proceeds to execute other tasks submitted. This makes sure actor is never blocked due to a long-running task. So if you are calling any async call inside test method and fear that the method will be executed on main thread then you have nothing to worry about. Since your ViewController class will be MainActor isolated your code becomes:
override func viewDidLoad() {
super.viewDidLoad()
Task {
await self.test()
}
}
func getBounds() -> CGRect {
let bounds = self.view.bounds
return bounds
}
func test() async {
// long running async calls
let bounds = self.getBounds()
// long running async calls
}
Now if some of your long running calls are synchronous then you have to remove test method from MainActor's isolation by applying nonisolated attribute. Also, creating Task with Task.init inherits the current actor context which is then executed on actor, to prevent this you have to use Task.detached to execute the test method:
override func viewDidLoad() {
super.viewDidLoad()
Task.detached {
await self.test()
}
}
func getBounds() -> CGRect {
let bounds = self.view.bounds
return bounds
}
nonisolated func test() async {
// long running async calls
// long running sync calls
let bounds = await self.getBounds()
// long running async calls
// long running sync calls
}
I had the same problem with a function which runs a long task and should always be run on a background thread. Ultimately using the new Swift async await syntax I could not find a easy way based on Task or TaskGroup to ensure this.
A Task.detached call will use a different thread as will the Task call itself. If this is called from the MainThread it will be another thread, if not it can be the main thread.
Ultimately I found a solution which always works - but looks very "hacky".
Make sure your background function is not isolated to the main thread by being part of a class that is a MainActor isolated class (like view controllers)
nonisolated func iAllwaysRunOnBackground() {}
Test for main thread and if executed on the main thread call the function again in a Task, wait for execution and return
nonisolated func iAllwaysRunOnBackground() async throws {
if Thread.isMainThread {
async let newTask = Task {
try await self.iAllwaysRunOnBackground()
}
let _ = try await newTask.value
return
}
function body
}

How to implement "Endless Task" in Swift

I'm new in Swift and I don't have much experience of it. And the thing is, I'm having an issue of high CPU usage (over 100% cpu usage in Xcode Debug session) when I add following codes.
To implement endless working thread, I used GCD (most recommended way in many articles) including while-loop structure.
private let engineQueue = DispatchQueue(label: "engine queue", qos: .userInitiated)
public func startEngine() {
engineQueue.async {
while true {
if (self.captureManager.isSessionRunning) {
guard let frame = self.captureManager.frameQueue.popFrame(),
let buf = frame.getYPlanar() else {
continue
}
// process a frame
self.engine.processNewFrame(buf: buf, width: Int32(frame.cols!), height: Int32(frame.rows!))
}
}
}
}
And I noticed that this is a terrible practice (high CPU usage), and there must be a way better than this.
So questions are
Do I have to use GCD rather than Thread in this case?
What is the best way to work for this?
Thanks,

Delay in unit test

So I have a unit test to test if clinics are being updated every 10 seconds. After 5 seconds, I clear all of the clinics. Then set an expectation that times out after 9 seconds to make sure clinics were updated. Here is my code:
func testRefresh() {
let expec = expectation(description: "Clinics timer expectation")
let expec2 = expectation(description: "Clinics timer expectation2")
expec2.isInverted = true
let dispatchGroup = DispatchGroup(count: 5)
dataStore.load()
wait(for: [expec2], timeout: 5.0) // This is what I am asking about
self.dataStore.clinicsSignal.fire([])
dataStore.clinicsSignal.subscribeOnce(with: dispatchGroup) {
print("clinics signal = \($0)")
expec.fulfill()
}
wait(for: [expec], timeout: 9.0)
XCTAssertFalse(self.dataStore.clinics.isEmpty)
}
I want to have that delay for 5 seconds. Using an inverted expectation the way I did is the only way I could find to make it work. I just think using an inverted expectation is bad practice.
If I use sleep(5) it stops the whole program for 5 seconds. I have also tried a solution using DispatchQueue.main.asyncAfter like outlined here but to no avail.
I have two suggestions to use together:
Use a spy test double to make sure that the service your data store uses to refresh the clinics is called twice
Inject the refresh interval to make the tests faster
Spy test double
Testing the side effect of the data loading, that it hits the service, could be a way to simplify your test.
Instead of using different expectations and exercising the system under test in a way that might not be what happens at runtime (the dataStore.clinicsSignal.fire([])) you can just count how many times the service is hit, and assert the value is 2.
Inject refresh interval
The approach I would recommend is to inject the time setting for how frequently the clinics should be updated in the class, and then set a low value in the tests.
After all, I'm guessing what you are interested in is that the update code runs as expected, not every 10 seconds. That is, it should update at the frequency you set.
You could do this by having the value as a default in the init of your data store, and then override it in the tests.
The reason I'm suggesting to use a shorter refresh interval is that in the context of unit testing, the faster they run the better it is. You want the feedback loop to be as fast as possible.
Putting it all together, something more or less like this
protocol ClinicsService {
func loadClinics() -> SignalProducer<[Clinics], ClinicsError>
}
class DataSource {
init(clinicsService: ClinicsService, refreshInterval: TimeInterval = 5) { ... }
}
// in the tests
class ClinicsServiceSpy: ClinicsService {
private(var) callsCount: Int = 0
func loadClinics() -> SignalProducer<[Clinics], ClinicsError> {
callsCount += 1
// return some fake data
}
}
func testRefresh() {
let clinicsServiceSpy = ClinicsServiceSpy()
let dataStore = DataStore(clinicsService: clinicsServiceSpy, refreshInterval: 0.05)
// This is an async expectation to make sure the call count is the one you expect
_ = expectation(
for: NSPredicate(
block: { input, _ -> Bool in
guard let spy = input as? ClinicsServiceSpy else { return false }
return spy.callsCount == 2
),
evaluatedWith: clinicsServiceSpy,
handler: .none
)
dataStore.loadData()
waitForExpectations(timeout: .2, handler: nil)
}
If you also used Nimble to have a more refined expectation API your test could look like this:
func testRefresh() {
let clinicsServiceSpy = ClinicsServiceSpy()
let dataStore = DataStore(clinicsService: clinicsServiceSpy, refreshInterval: 0.05)
dataStore.loadData()
expect(clinicsServiceSpy.callsCount).toEventually(equal(2))
}
The tradeoff you make in this approach is to make the test more straightforward by writing a bit more code. Whether it's a good tradeoff is up to you do decide.
I like working in this way because it keeps each component in my system free from implicit dependencies and the test I end up writing are easy to read and work as a living documentation for the software.
Let me know what you think.

Swift DispatchQueue concurrentPerform OpenGL parallel rendering

I have a headless EGL renderer in c++ for Linux that I have wrapped with bindings to use in Swift. It works great – I can render in parallel creating multiple contexts and rendering in separate threads, but I've run into a weird issue. First of all I have wrapped all GL calls specific to a renderer and it's context inside it's own serial queue like below.
func draw(data:Any) -> results {
serial.sync {
//All rendering code for this renderer is wrapped in a unique serial queue.
bindGLContext()
draw()
}
}
To batch data between renderers I used DispatchQueue.concurrentPerform. It works correctly, but when I try creating a concurrent queue with a DispatchGroup something weird happens. Even though I have wrapped all GL calls in serial queues the GL contexts get messed up and all gl calls fail to allocate textures/buffers/etc.
So I am trying to understand the difference between these two and why one works and the other doesn't. Any ideas would be great!
//This works
DispatchQueue.concurrentPerform(iterations: renderers.count) { j in
let batch = batches[j]
let renderer = renderers[j]
let _ = renderer.draw(data:batch)
}
//This fails – specifically GL calls fail
let group = DispatchGroup()
let q = DispatchQueue(label: "queue.concurrent", attributes: .concurrent)
for (j, renderer) in renderers.enumerated() {
q.async(group: group) {
let batch = batches[j]
let _ = renderer.draw(data:batch)
}
}
group.wait()
Edit:
I would make sure the OpenGL wrapper is actually thread safe. Each renderer having it's own serial queue may not help if the multiple renderers are making OpenGL calls simultaneously. It's possible the DispatchQueue.concurrentPerform version works because it is just running serially.
Original answer:
I suspect the OpenGL failures have to do with hitting memory constraints. When you dispatch many tasks to a concurrent queue, GCD doesn't do anything clever to rate-limit the number of tasks that are started. If a bunch of running tasks are blocked doing IO, it may just start more and more tasks before any of them finish, gobbling up more and more memory. Here's a detailed write-up from Mike Ash about the problem.
I would guess that DispatchQueue.concurrentPerform works because it has some kind of extra logic internally to avoid spawning too many threads, though it's not well documented and there may be platform-specific stuff happening here. I'm not sure why the function would even exist if all it was doing was dispatching to a concurrent queue.
If you want to dispatch a large number of items directly to a DispatchQueue, especially if those items have some non-CPU-bound component to them, you need to add some extra logic yourself to limit the number of tasks that get started. Here's an example from Soroush Khanlou's GCD Handbook:
class LimitedWorker {
private let serialQueue = DispatchQueue(label: "com.khanlou.serial.queue")
private let concurrentQueue = DispatchQueue(label: "com.khanlou.concurrent.queue", attributes: .concurrent)
private let semaphore: DispatchSemaphore
init(limit: Int) {
semaphore = DispatchSemaphore(value: limit)
}
func enqueue(task: #escaping () -> ()) {
serialQueue.async(execute: {
self.semaphore.wait()
self.concurrentQueue.async(execute: {
task()
self.semaphore.signal()
})
})
}
}
It uses a sempahore to limit the number of concurrent tasks that are executing on the concurrent queue, and uses a serial queue to feed new tasks to the concurrent queue. Newly enqueued tasks block at self.semaphore.wait() if the maximum number of tasks are already scheduled on the concurrent queue.
You would use it like this:
let group = DispatchGroup()
let q = LimitedWorker(limit: 10) // Experiment with this number
for (j, renderer) in renderers.enumerated() {
group.enter()
q.enqueue {
let batch = batches[j]
let _ = renderer.draw(data:batch)
group.leave()
}
}
group.wait()

How do I dispatch_sync, dispatch_async, dispatch_after, etc in Swift 3, Swift 4, and beyond?

I have lots of code in Swift 2.x (or even 1.x) projects that looks like this:
// Move to a background thread to do some long running work
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)) {
let image = self.loadOrGenerateAnImage()
// Bounce back to the main thread to update the UI
dispatch_async(dispatch_get_main_queue()) {
self.imageView.image = image
}
}
Or stuff like this to delay execution:
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, Int64(0.5 * Double(NSEC_PER_SEC))), dispatch_get_main_queue()) {
print("test")
}
Or any of all kinds of other uses of the Grand Central Dispatch API...
Now that I've opened my project in Xcode 8 (beta) for Swift 3, I get all kinds of errors. Some of them offer to fix my code, but not all of the fixes produce working code. What do I do about this?
Since the beginning, Swift has provided some facilities for making ObjC and C more Swifty, adding more with each version. Now, in Swift 3, the new "import as member" feature lets frameworks with certain styles of C API -- where you have a data type that works sort of like a class, and a bunch of global functions to work with it -- act more like Swift-native APIs. The data types import as Swift classes, their related global functions import as methods and properties on those classes, and some related things like sets of constants can become subtypes where appropriate.
In Xcode 8 / Swift 3 beta, Apple has applied this feature (along with a few others) to make the Dispatch framework much more Swifty. (And Core Graphics, too.) If you've been following the Swift open-source efforts, this isn't news, but now is the first time it's part of Xcode.
Your first step on moving any project to Swift 3 should be to open it in Xcode 8 and choose Edit > Convert > To Current Swift Syntax... in the menu. This will apply (with your review and approval) all of the changes at once needed for all the renamed APIs and other changes. (Often, a line of code is affected by more than one of these changes at once, so responding to error fix-its individually might not handle everything right.)
The result is that the common pattern for bouncing work to the background and back now looks like this:
// Move to a background thread to do some long running work
DispatchQueue.global(qos: .userInitiated).async {
let image = self.loadOrGenerateAnImage()
// Bounce back to the main thread to update the UI
DispatchQueue.main.async {
self.imageView.image = image
}
}
Note we're using .userInitiated instead of one of the old DISPATCH_QUEUE_PRIORITY constants. Quality of Service (QoS) specifiers were introduced in OS X 10.10 / iOS 8.0, providing a clearer way for the system to prioritize work and deprecating the old priority specifiers. See Apple's docs on background work and energy efficiency for details.
By the way, if you're keeping your own queues to organize work, the way to get one now looks like this (notice that DispatchQueueAttributes is an OptionSet, so you use collection-style literals to combine options):
class Foo {
let queue = DispatchQueue(label: "com.example.my-serial-queue",
attributes: [.serial, .qosUtility])
func doStuff() {
queue.async {
print("Hello World")
}
}
}
Using dispatch_after to do work later? That's a method on queues, too, and it takes a DispatchTime, which has operators for various numeric types so you can just add whole or fractional seconds:
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { // in half a second...
print("Are we there yet?")
}
You can find your way around the new Dispatch API by opening its interface in Xcode 8 -- use Open Quickly to find the Dispatch module, or put a symbol (like DispatchQueue) in your Swift project/playground and command-click it, then brouse around the module from there. (You can find the Swift Dispatch API in Apple's spiffy new API Reference website and in-Xcode doc viewer, but it looks like the doc content from the C version hasn't moved into it just yet.)
See the Migration Guide for more tips.
In Xcode 8 beta 4 does not work...
Use:
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
print("Are we there yet?")
}
for async two ways:
DispatchQueue.main.async {
print("Async1")
}
DispatchQueue.main.async( execute: {
print("Async2")
})
This one is good example for Swift 4 about async:
DispatchQueue.global(qos: .background).async {
// Background Thread
DispatchQueue.main.async {
// Run UI Updates or call completion block
}
}
in Xcode 8 use:
DispatchQueue.global(qos: .userInitiated).async { }
Swift 5.2, 4 and later
Main and Background Queues
let main = DispatchQueue.main
let background = DispatchQueue.global()
let helper = DispatchQueue(label: "another_thread")
Working with async and sync threads!
background.async { //async tasks here }
background.sync { //sync tasks here }
Async threads will work along with the main thread.
Sync threads will block the main thread while executing.
Swift 4.1 and 5. We use queues in many places in our code. So, I created Threads class with all queues. If you don't want to use Threads class you can copy the desired queue code from class methods.
class Threads {
static let concurrentQueue = DispatchQueue(label: "AppNameConcurrentQueue", attributes: .concurrent)
static let serialQueue = DispatchQueue(label: "AppNameSerialQueue")
// Main Queue
class func performTaskInMainQueue(task: #escaping ()->()) {
DispatchQueue.main.async {
task()
}
}
// Background Queue
class func performTaskInBackground(task:#escaping () throws -> ()) {
DispatchQueue.global(qos: .background).async {
do {
try task()
} catch let error as NSError {
print("error in background thread:\(error.localizedDescription)")
}
}
}
// Concurrent Queue
class func perfromTaskInConcurrentQueue(task:#escaping () throws -> ()) {
concurrentQueue.async {
do {
try task()
} catch let error as NSError {
print("error in Concurrent Queue:\(error.localizedDescription)")
}
}
}
// Serial Queue
class func perfromTaskInSerialQueue(task:#escaping () throws -> ()) {
serialQueue.async {
do {
try task()
} catch let error as NSError {
print("error in Serial Queue:\(error.localizedDescription)")
}
}
}
// Perform task afterDelay
class func performTaskAfterDealy(_ timeInteval: TimeInterval, _ task:#escaping () -> ()) {
DispatchQueue.main.asyncAfter(deadline: (.now() + timeInteval)) {
task()
}
}
}
Example showing the use of main queue.
override func viewDidLoad() {
super.viewDidLoad()
Threads.performTaskInMainQueue {
//Update UI
}
}