Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Concurrency in Swift

Teddy Ku
December 07, 2017

Concurrency in Swift

Lightning Talk going over the Swift Concurrency Manifesto, and possible applications

Teddy Ku

December 07, 2017
Tweet

More Decks by Teddy Ku

Other Decks in Programming

Transcript

  1. It’s almost 2018! • iPhone X: 6 cores, 3 GB

    RAM • Multi-core, multi-thread apps • Scalable, Distributed applications https://farm8.staticflickr.com/7403/16384510489_1cda4743b5_o.jpg
  2. Concurrency in Swift 4 func processImageData1(completionBlock: (result: Image) -> Void)

    { loadWebResource("dataprofile.txt") { dataResource in loadWebResource("imagedata.dat") { imageResource in decodeImage(dataResource, imageResource) { imageTmp in dewarpAndCleanupImage(imageTmp) { imageResult in completionBlock(imageResult) } } } } }
  3. Concurrency in Swift 4 • Completion blocks • Queues, Threads

    • Mutex, Semaphore • GCD: dispatch_barrier
  4. async/await func loadWebResource(_ path: String) async -> Resource func decodeImage(_

    r1: Resource, _ r2: Resource) async -> Image func dewarpAndCleanupImage(_ i : Image) async -> Image func processImageData1() async -> Image { let dataResource = await loadWebResource("dataprofile.txt") let imageResource = await loadWebResource("imagedata.dat") let imageTmp = await decodeImage(dataResource, imageResource) let imageResult = await dewarpAndCleanupImage(imageTmp) return imageResult }
  5. async/await func loadWebResource(_ path: String) async -> Resource func processImageData1()

    async -> Image { let dataResource = await loadWebResource("dataprofile.txt") // ... } • async: the func can suspend before returning a value (is a coroutine) • await: noop, but shows coder that non-local control flow can happen
  6. Actors: in Swift 1. DispatchQueue 2. Data that queue protects

    3. Messages that can be run on that queue
 
 `actor`: reference type, like `class`. Can conform to protocols
  7. actors actor TableActor { let mainActor : TheMainActor var theList

    : [String] = [] { didSet { mainActor.updateTableView(theList) } } init(mainActor: TheMainActor) { self.mainActor = mainActor } // this checks to see if all the entries in the list are capitalized: // if so, it capitalize the string before returning it to encourage // capitalization consistency in the list. func prettify(_ x : String) -> String { // Details omitted: it inspects theList, adjusting the // string before returning it if necessary. } actor func add(entry: String) { theList.append(prettify(entry)) } }
  8. actors actor TableActor { let mainActor : TheMainActor var theList

    : [String] = [] { didSet { mainActor.updateTableView(theList) } }
 
 func prettify(_ x : String) -> String {} actor func add(entry: String) { theList.append(prettify(entry)) } } // 1. send message to Table actor tableActor.add(entry: “foo") // 2. Table actor modifies its own data // 3. Table actor sends message to Main actor with copied data
 // 4. Main actor refreshes table view
  9. actors • TableActor’s data: reference to mainActor, and theList •

    actor can send messages to any other actor • actor methods are the messages that actors accept • actor methods are implicitly async, so they can freely call async methods and await their results • An actor method cannot return a value, throw an error, or have an inout parameter. • Actor method parameters must produce independent values when copied • Local state and non-actor methods may only be accessed by methods defined lexically on the actor or in an extension to it (whether they are marked actor or otherwise) actor func add(entry: String) mainActor.updateTableView(theList)
  10. • Compiler optimization • detect deadlocks • optimize ARC retain/release

    calls • Better debugging • Spread knowledge First-class concurrency model benefits
  11. Before: completion block on DispatchQueue.main @objc final class RemoteImageManager: NSObject

    { static func downloadImage(with url: URL, mainQueueCompletion completion: @escaping PINRemoteImageManagerImageCompletion) -> UUID? { return PINRemoteImageManager.shared().downloadImage(with: url, options: [], completion: { (result: PINRemoteImageManagerResult) in DispatchQueue.main.async { completion(result) } }) } }
  12. After: RemoteImageActor actor RemoteImageActor { // implicitly private let cache:

    ImageCache actor func downloadImage(with url: URL) -> RemoteImageManagerResult { // urlSession stuff // handle updating cache without mutex/locks because this actor has exclusive access } } class ViewController: UIViewContoller { private var remoteImageActor: RemoteImageActor!
 private var imageViewActor: ImageViewActor! private func updateBackground() { let dogeImage = await remoteImageActor.downloadImage(with: "doge.gif") let processedImage = await imageProcessorActor.scale(0.5, image: dogeImage) imageViewActor.updateImageView(with image: dogeImage) } }
  13. Actor benefits • clearly express which queue is running •

    avoid mutex/locks • avoid programmer error
  14. Still to come • Reliability through fault isolation • fire

    and forget actor messages • add error handling in Actor init, not scattered everywhere • handle failures by restarting actor, etc. • Improving system architecture • declarative, clean interprocess communication • `distributed` keyword