Conncurency and Multithreading - toant-dev/toandev.github.io GitHub Wiki
Overview
Grand Central Dispatch (GCD)
GCD abstracts away thread management code and moves it down to the system level, exposing a light API to define tasks and execute them on an appropriate dispatch queue.
Serial Queues: The Main Thread
Background Threads
DispatchQueue.global(qos: .userInitiated).async { [unowned self] in
self.compute()
}
Concurrent Queues
let queue = DispatchQueue(label: "com.app.concurrentQueue", attributes: .concurrent)
queue.async { task() }
Parallelize: split to multi-threads
Parallelization of N Tasks
let kMaxConcurrent = 3 // Or 1 if you want strictly ordered downloads!
let semaphore = DispatchSemaphore(value: kMaxConcurrent)
let downloadQueue = DispatchQueue(label: "com.app.downloadQueue", attributes: .concurrent)
class ViewController: UIViewController {
@IBAction func handleTap(_ sender: Any) {
for i in 0..<15 {
downloadQueue.async { [unowned self] in
// Lock shared resource access
semaphore.wait()
// Expensive task
self.download(i + 1)
// Update the UI on the main thread, always!
DispatchQueue.main.async {
tableView.reloadData()
// Release the lock
semaphore.signal()
}
}
}
}
func download(_ songId: Int) -> Void {
var counter = 0
// Simulate semi-random download times.
for _ in 0..<Int.random(in: 999999...10000000) {
counter += songId
}
}
}
OperationQueue
If we want to create a repeatable, structured, long-running task that produces associated state or data? And what if we want to model this chain of operations such that they can be cancelled, suspended and tracked, while still working with a closure-friendly API
class ViewController: UIViewController {
var queue = OperationQueue()
var rawImage = UIImage? = nil
let imageUrl = URL(string: "https://example.com/portrait.jpg")!
@IBOutlet weak var imageView: UIImageView!
let downloadOperation = BlockOperation {
let image = Downloader.downloadImageWithURL(url: imageUrl)
OperationQueue.main.async {
self.rawImage = image
}
}
let filterOperation = BlockOperation {
let filteredImage = ImgProcessor.addGaussianBlur(self.rawImage)
OperationQueue.main.async {
self.imageView = filteredImage
}
}
filterOperation.addDependency(downloadOperation)
[downloadOperation, filterOperation].forEach {
queue.addOperation($0)
}
}