DEV Community

Naveen Ragul B
Naveen Ragul B

Posted on • Updated on

Swift - Concurrency

  • Asynchronous code - can be suspended and resumed later.
  • Parallel code - multiple pieces of code run simultaneously.
  • A program that uses parallel and asynchronous code carries out multiple operations at a time; it suspends operations that are waiting for an external system, and makes it easier to write this code in a memory-safe way.
  • concurrency to refer to this common combination of asynchronous and parallel code.

Asynchronous Functions

  • synchronous functions and methods, which either run to completion, throw an error, or never return.
  • Asynchronous function or method still does one of those three things, but that can be suspended while it’s partway through execution.

  • To indicate that a function or method is asynchronous, you write the async keyword in its declaration.

  • When calling an asynchronous method, execution suspends until that method returns. Use await in front of the call to mark the possible suspension point

example :

func listPhotos(inGallery name: String) async -> [String] {
    let result = // some asynchronous networking code
    return result
}
Enter fullscreen mode Exit fullscreen mode
let photoNames = await listPhotos(inGallery: "Summer Vacation")
let sortedNames = photoNames.sorted()
let name = sortedNames[0]
let photo = await downloadPhoto(named: name)
show(photo)
Enter fullscreen mode Exit fullscreen mode

Places in your program can call asynchronous functions or methods:

  1. Code in the body of an asynchronous function, method, or property.
  2. Code in the static main() method of a structure, class, or enumeration that’s marked with @main.
  3. Code in an unstructured child task

In above example, Instead of returning whole array at once after all of the array’s elements are ready, wait for one element of the collection at a time using an asynchronous sequence using for-await-in loop.

example :

import Foundation

let handle = FileHandle.standardInput
for try await line in handle.bytes.lines {
    print(line)
}
Enter fullscreen mode Exit fullscreen mode

for-await-in loop can be used for any types that conforms to the AsyncSequence protocol. Similarly for-in for Sequence.


Calling Asynchronous Functions in Parallel

  • Calling an asynchronous function with await runs only one piece of code at a time.

  • To call an asynchronous function and let it run in parallel with code around it, write async in front of let when you define a constant, and then write await each time you use the constant.

example :

async let firstPhoto = downloadPhoto(named: photoNames[0])
async let secondPhoto = downloadPhoto(named: photoNames[1])
async let thirdPhoto = downloadPhoto(named: photoNames[2])

let photos = await [firstPhoto, secondPhoto, thirdPhoto]
show(photos)
Enter fullscreen mode Exit fullscreen mode
  • Call asynchronous functions with await when the code on the following lines depends on that function’s result. This creates work that is carried out sequentially.
  • Call asynchronous functions with async-let when you don’t need the result until later in your code. This creates work that can be carried out in parallel.

Structured Concurrency

A task is a unit of work that can be run asynchronously as part of your program. All asynchronous code runs as part of some task.

  • Task can be added as a child to a Task Group.
  • async-let syntax creates a child task.
  • Each task in a task group has the same parent task, and each task can have child tasks. Because of the explicit relationship between tasks and task groups, this approach is called structured concurrency

Unstructured Concurrency

  • Unstructured task doesn’t have a parent task.
  • To create an unstructured task that runs on the current actor, call the Task.init(priority:operation:) initializer.
  • To create an unstructured task that’s not part of the current actor, known more specifically as a detached task, call the Task.detached(priority:operation:) class method.

Actors

  • Actors are reference types.
  • Unlike classes, actors allow only one task to access their mutable state at a time, which makes it safe for code in multiple tasks to interact with the same instance of an actor.
  • You create an instance of an actor using the same initializer syntax as structures and classes. When you access a property or method of an actor, you use await to mark the potential suspension point.

example :

actor TemperatureLogger {
    let label: String
    var measurements: [Int]
    private(set) var max: Int

    init(label: String, measurement: Int) {
        self.label = label
        self.measurements = [measurement]
        self.max = measurement
    }
}
Enter fullscreen mode Exit fullscreen mode
let logger = TemperatureLogger(label: "Outdoors", measurement: 25)
print(await logger.max)
Enter fullscreen mode Exit fullscreen mode

Top comments (0)