Search code examples
swiftgrand-central-dispatchswift-concurrency

What's difference btw DispatchQueue.global(qos: .background).async {} and Task(priority: .background) {} in Swift


I can see Swift support 2 kinds of background task

Using DispatchQueue

DispatchQueue.global(qos: .background).async {}

Using Task

Task(priority: .background) {} 

So I am confusing about what's the difference and which one should I use?


Solution

  • These are two completely different patterns (despite the superficially similar reference to a .background QoS/priority). These two approaches are used in entirely different situations. This is a gross over-simplification, but:

    • The dispatch queue example is used in legacy, GCD codebases (those which have not (yet) adopted “Swift concurrency”) and you want to launch a unit of work that is, itself, synchronous but you want to avoid blocking the current thread; and

    • The Task {…} is used in contemporary Swift concurrency codebases, generally to launch a task that, itself, consists of asynchronous code (notably, code that will await some other async method) … we use Task {…} either to bridge from a synchronous context to an asynchronous one, or to enjoy some fine-grained control of unstructured concurrency (at the cost of greater complexity).

    So, the question is more fundamental than DispatchQueue.global.async {…} vs Task {…}. The real question is GCD vs Swift concurrency. (And we almost always avoid intermixing GCD with Swift concurrency, except where absolutely necessary, but that is beyond the scope of this question. We would generally pick either GCD or Swift concurrency, and use that particular tech stack consistently within the given project.)

    In contemporary codebases, we would adopt Swift concurrency (and thus favor Task {…}, or, where you can, stick with structured concurrency), but if you are saddled with some legacy GCD codebase, then you might use global dispatch queues for the slow/synchronous work.


    By the way, I would advise being a little more clear with the term “background task”. Specifically, I might encourage you to avoid conflating the term with a Swift concurrency TaskPriority of .background or a global dispatch queue with a QoSClass of .background).

    Traditionally, when we abstractly talk about a “background task” we mean “get it off the current thread”. But, the task “priority” and/or the dispatch queue “quality of service” is more of a question of the relative priority of this work with respect to other work also submitted.


    Finally, it is worth stating that Task {…} does not get work off the current context. In fact, quite the opposite is true: Task {…} will run the work on behalf of the current actor. Using Task.detached {…} is probably closer to the idea of a global dispatch queue, but even that is an imperfect analogy.

    For more information, see The Swift Programming Language: Concurrency: Unstructured Concurrency or WWDC 2021 videos Meet async/await and Swift concurrency: Behind the scenes.