Ask Sawal

Discussion Forum
Notification Icon1
Write Answer Icon
Add Question Icon

What is concurrency in ios?

5 Answer(s) Available
Answer # 1 #

In particular, I wish to explore the differences between serial and concurrent queues, as well as the differences between synchronous and asynchronous execution.

If you've never used GCD before, this article is a great place to start. If you have some experience with GCD, but are still curious about the topics mentioned above, I think you will still find it useful. And I hope you will pick up one or two new things along the way.

I created a SwiftUI companion app to visually demonstrate the concepts in this article. The app also has a fun short quiz that I encourage you to try before and after reading this article. Download the source code here, or get the public beta here.

I will begin with an introduction to GCD, followed by a detailed explanation on sync, async, serial and concurrent. Afterwards, I will cover some pitfalls when working with concurrency. Finally, I will end with a summary and some general advice.

Let’s start with a brief intro to GCD and dispatch queues. Feel free to skip to the Sync vs Async section if you are already familiar with the topic.

Concurrency lets you take advantage of the fact that your device has multiple CPU cores. To make use of these cores, you will need to use multiple threads. However, threads are a low-level tool, and managing threads manually in an efficient manner is extremely difficult.

Grand Central Dispatch was created by Apple over 10 years ago as an abstraction to help developers write multi-threaded code without manually creating and managing the threads themselves.

With GCD, Apple took an asynchronous design approach to the problem. Instead of creating threads directly, you use GCD to schedule work tasks, and the system will perform these tasks for you by making the best use of its resources. GCD will handle creating the requisite threads and will schedule your tasks on those threads, shifting the burden of thread management from the developer to the system.

A big advantage of GCD is that you don’t have to worry about hardware resources as you write your concurrent code. GCD manages a thread pool for you, and it will scale from a single-core Apple Watch all the way up to a many-core MacBook Pro.

These are the main building blocks of GCD which let you execute arbitrary blocks of code using a set of parameters that you define. The tasks in dispatch queues are always started in a first-in, first-out (FIFO) fashion. Note that I said started, because the completion time of your tasks depends on several factors, and is not guaranteed to be FIFO (more on that later.)

Broadly speaking, there are three kinds of queues available to you:

Every app comes with a Main queue, which is a serial queue that executes tasks on the main thread. This queue is responsible for drawing your application’s UI and responding to user interactions (touch, scroll, pan, etc.) If you block this queue for too long, your iOS app will appear to freeze, and your macOS app will display the infamous beach ball/spinning wheel.

When performing a long-running task (network call, computationally intensive work, etc), we avoid freezing the UI by performing this work on a background queue. Then we update the UI with the results on the main queue:

As a rule of thumb, all UI work must be executed on the Main queue. You can turn on the Main Thread Checker option in Xcode to receive warnings whenever UI work gets executed on a background thread.

In addition to the main queue, every app comes with several pre-defined concurrent queues that have varying levels of Quality of Service (an abstract notion of priority in GCD.)

For example, here’s the code to submit work asynchronously to the user interactive (highest priority) QoS queue:

Alternatively, you can call the default priority global queue by not specifying a QoS like this:

Additionally, you can create your own private queues using the following syntax:

When creating private queues, it helps to use a descriptive label (such as reverse DNS notation), as this will aid you while debugging in Xcode’s navigator, lldb, and Instruments:

By default, private queues are serial (I’ll explain what this means shortly, promise!) If you want to create a private concurrent queue, you can do so via the optional attributes parameter:

There is an optional QoS parameter as well. The private queues that you create will ultimately land in one of the global concurrent queues based on their given parameters.

I mentioned dispatching tasks to queues. Tasks can refer to any block of code that you submit to a queue using the sync or async functions. They can be submitted in the form of an anonymous closure:

Or inside a dispatch work item that gets performed later:

Regardless of whether you dispatch synchronously or asynchronously, and whether you choose a serial or concurrent queue, all of the code inside a single task will execute line by line. Concurrency is only relevant when evaluating multiple tasks.

For example, if you have 3 loops inside the same task, these loops will always execute in order:

This code always prints out ten digits from 0 to 9, followed by ten blue circles, followed by ten broken hearts, regardless of how you dispatch that closure.

Individual tasks can also have their own QoS level as well (by default they use their queue’s priority.) This distinction between queue QoS and task QoS leads to some interesting behaviour that we will discuss in the priority inversion section.

By now you might be wondering what serial and concurrent are all about. You might also be wondering about the differences between sync and async when submitting your tasks. This brings us to the crux of this article, so let’s dive in!

When you dispatch a task to a queue, you can choose to do so synchronously or asynchronously using the sync and async dispatch functions. Sync and async primarily affect the source of the submitted task, that is the queue where it is being submitted from.

When your code reaches a sync statement, it will block the current queue until that task completes. Once the task returns/completes, control is returned to the caller, and the code that follows the sync task will continue.

Think of sync as synonymous with ‘blocking’.

An async statement, on the other hand, will execute asynchronously with respect to the current queue, and immediately returns control back to the caller without waiting for the contents of the async closure to execute. There is no guarantee as to when exactly the code inside that async closure will execute.

It may not be obvious what the source, or current, queue is, because it’s not always explicitly defined in the code.

For example, if you call your sync statement inside viewDidLoad, your current queue will be the Main dispatch queue. If you call the same function inside a URLSession completion handler, your current queue will be a background queue.

Going back to sync vs async, let’s take this example:

The above code will block the current queue, enter the closure and execute its code on the global queue by printing “Inside”, before proceeding to print “Outside”. This order is guaranteed.

Let’s see what happens if we try async instead:

Our code now submits the closure to the global queue, then immediately proceeds to run the next line. It will likely print “Outside” before “Inside”, but this order isn’t guaranteed. It depends on the QoS of the source and destination queues, as well as other factors that the system controls.

Threads are an implementation detail in GCD — we do not have direct control over them and can only deal with them using queue abstractions. Nevertheless, I think it can be useful to ‘peek under the covers’ at thread behaviour to understand some challenges we might encounter with GCD.

For instance, when you submit a task using sync, GCD optimizes performance by executing that task on the current thread (the caller.)

There is one exception however, which is when you submit a sync task to the main queue —  doing so will always run the task on the main thread and not the caller. This behaviour can have some ramifications that we will explore in the priority inversion section.

When submitting work to a queue, Apple recommends using asynchronous execution over synchronous execution. However, there are situations where sync might be the better choice, such as when dealing with race conditions, or when performing a very small task. I will cover these situations shortly.

One large consequence of performing work asynchronously inside a function is that the function can no longer directly return its values (if they depend on the async work that’s being done). It must instead use a closure/completion handler parameter to deliver the results.

To demonstrate this concept, let’s take a small function that accepts image data, performs some expensive computation to process the image, then returns the result:

In this example, the function upscaleAndFilter(image:) might take several seconds, so we want to offload it into a separate queue to avoid freezing the UI. Let’s create a dedicated queue for image processing, and then dispatch the expensive function asynchronously:

There are two issues with this code. First, the return statement is inside the async closure, so it is no longer returning a value to the processImageAsync(data:) function, and currently serves no purpose.

But the bigger issue is that our processImageAsync(data:) function is no longer returning any value, because the function reaches the end of its body before it enters the async closure.

To fix this error, we will adjust the function so that it no longer directly returns a value. Instead, it will have a new completion handler parameter that we can call once our asynchronous function has completed its work:

As evident in this example, the change to make the function asynchronous has propagated to its caller, who now has to pass in a closure and handle the results asynchronously as well. By introducing an asynchronous task, you can potentially end up modifying a chain of several functions.

Concurrency and asynchronous execution add complexity to your project as we just observed. This indirection also makes debugging more difficult. That’s why it really pays off to think about concurrency early in your design — it’s not something you want to tack on at the end of your design cycle.

Synchronous execution, by contrast, does not increase complexity. Rather, it allows you to continue using return statements as you did before. A function containing a sync task will not return until the code inside that task has completed. Therefore it does not require a completion handler.

If you are submitting a tiny task (for example, updating a value), consider doing it synchronously. Not only does that help you keep your code simple, it will also perform better — Async is believed to incur an overhead that outweighs the benefit of doing the work asynchronously for tiny tasks that take under 1ms to complete.

If you are submitting a large task, however, like the image processing we performed above, then consider doing it asynchronously to avoid blocking the caller for too long.

While it is safe to dispatch a task asynchronously from a queue into itself (for example, you can use .asyncAfter on the current queue), you can not dispatch a task synchronously from a queue into the same queue. Doing so will result in a deadlock that immediately crashes the app!

This issue can manifest itself when performing a chain of synchronous calls that lead back to the original queue. That is, you sync a task onto another queue, and when the task completes, it syncs the results back into the original queue, leading to a deadlock. Use async to avoid such crashes.

Dispatching tasks synchronously from the main queue will block that queue, thereby freezing the UI, until the task is completed. Thus it’s better to avoid dispatching work synchronously from the main queue unless you’re performing really light work.

Serial and concurrent affect the destination —  the queue in which your work has been submitted to run. This is in contrast to sync and async, which affected the source.

A serial queue will not execute its work on more than one thread at a time, regardless of how many tasks you dispatch on that queue. Consequently, the tasks are guaranteed to not only start, but also terminate, in first-in, first-out order.

Moreover, when you block a serial queue (using a sync call, semaphore, or some other tool), all work on that queue will halt until the block is over.

A concurrent queue can spawn multiple threads, and the system decides how many threads are created. Tasks always start in FIFO order, but the queue does not wait for tasks to finish before starting the next task, therefore tasks on concurrent queues can finish in any order.

When you perform a blocking command on a concurrent queue, it will not block the other threads on this queue. Additionally, when a concurrent queue gets blocked, it runs the risk of thread explosion. I will cover this in more detail later on.

The main queue in your app is serial. All the global pre-defined queues are concurrent. Any private dispatch queue you create is serial by default, but can be set to be concurrent using an optional attribute as discussed earlier.

It’s important to note here that the concept of serial vs concurrent is only relevant when discussing a specific queue. All queues are concurrent relative to each other.

That is, if you dispatch work asynchronously from the main queue to a private serial queue, that work will be completed concurrently with respect to the main queue. And if you create two different serial queues, and then perform blocking work on one of them, the other queue is unaffected.

To demonstrate the concurrency of multiple serial queues, let’s take this example:

Both queues here are serial, but the results are jumbled up because they execute concurrently in relation to each other. The fact that they’re each serial (or concurrent) has no effect on this result. Their QoS level determines who will generally finish first (order not guaranteed).

If we want to ensure that the first loop finishes first before starting the second loop, we can submit the first task synchronously from the caller:

This is not necessarily desirable, because we are now blocking the caller while the first loop is executing.

To avoid blocking the caller, we can submit both tasks asynchronously, but to the same serial queue:

Now our tasks execute concurrently with respect to the caller, while also keeping their order intact.

Note that if we make our single queue concurrent via the optional parameter, we go back to the jumbled results, as expected:

Sometimes you might confuse synchronous execution with serial execution (at least I did), but they are very different things. For example, try changing the first dispatch on line 3 from our previous example to a sync call:

Suddenly, our results are back in perfect order. But this is a concurrent queue, so how could that happen? Did the sync statement somehow turn it into a serial queue?

The answer is no!

This is a bit sneaky. What happened is that we did not reach the async call until the first task had completed its execution. The queue is still very much concurrent, but inside this zoomed-in section of the code. it appears as if it were serial. This is because we are blocking the caller, and not proceeding to the next task, until the first one is finished.

If another queue somewhere else in your app tried submitting work to this same queue while it was still executing the sync statement, that work will run concurrently with whatever we got running here, because it’s still a concurrent queue.

Serial queues take advantage of CPU optimizations and caching, and help reduce context switching.

Apple recommends starting with one serial queue per subsystem in your app —  for example one for networking, one for file compression, etc. If the need arises, you can later expand to a hierarchy of queues per subsystem using the setTarget method or the optional target parameter when building queues.

If you run into a performance bottleneck, measure your app’s performance then see if a concurrent queue helps. If you do not see a measurable benefit, it’s better to stick to serial queues.

Priority inversion is when a high priority task is prevented from running by a lower priority task, effectively inverting their relative priorities.

This situation often occurs when a high QoS queue shares a resources with a low QoS queue, and the low QoS queue gets a lock on that resource.

But I wish to cover a different scenario that is more relevant to our discussion —  it’s when you submit tasks to a low QoS serial queue, then submit a high QoS task to that same queue. This scenario also results in priority inversion, because the high QoS task has to wait on the lower QoS tasks to finish.

GCD resolves priority inversion by temporarily raising the QoS of the queue that contains the low priority tasks that are ‘ahead’ of, or blocking, your high priority task.

It’s kind of like having cars stuck in front of an ambulance. Suddenly they’re allowed to cross the red light just so that the ambulance can move (in reality the cars move to the side, but imagine a narrow (serial) street or something, you get the point :-P)

To illustrate the inversion problem, let’s start with this code:

We create a starter queue (where we submit the tasks from), as well as two queues with different QoS. Then we dispatch tasks to each of these two queues, each task printing out an equal number of circles of a specific colour (utility queue is blue, background is white.)

Because these tasks are submitted asynchronously, every time you run the app, you’re going to see slightly different results. However, as you would expect, the queue with the lower QoS (background) almost always finishes last. In fact, the last 10–15 circles are usually all white.

But watch what happens when we submit a sync task to the background queue after the last async statement. You don’t even need to print anything inside the sync statement, just adding this line is enough:

The results in the console have flipped! Now, the higher priority queue (utility) always finishes last, and the last 10–15 circles are blue.

To understand why that happens, we need to revisit the fact that synchronous work is executed on the caller thread (unless you’re submitting to the main queue.)

In our example above, the caller (starterQueue) has the top QoS (userInteractive.) Therefore, that seemingly innocuous sync task is not only blocking the starter queue, but it’s also running on the starter’s high QoS thread. The task therefore runs with high QoS, but there are two other tasks ahead of it on the same background queue that have background QoS. Priority inversion detected!

As expected, GCD resolves this inversion by raising the QoS of the entire queue to temporarily match the high QoS task. Consequently, all the tasks on the background queue end up running at user interactive QoS, which is higher than the utility QoS. And that’s why the utility tasks finish last!

Side-note: If you remove the starter queue from that example and submit from the main queue instead, you will get similar results, as the main queue also has user interactive QoS.

To avoid priority inversion in this example, we need to avoid blocking the starter queue with the sync statement. Using async would solve that problem.

Although it’s not always ideal, you can minimize priority inversions by sticking to the default QoS when creating private queues or dispatching to the global concurrent queue.

When you use a concurrent queue, you run the risk of thread explosion if you’re not careful. This can happen when you try to submit tasks to a concurrent queue that is currently blocked (for example with a semaphore, sync, or some other way.) Your tasks will run, but the system will likely end up spinning up new threads to accommodate these new tasks, and threads aren’t cheap.

This is likely why Apple suggests starting with a serial queue per subsystem in your app, as each serial queue can only use one thread. Remember that serial queues are concurrent in relation to other queues, so you still get a performance benefit when you offload your work to a queue, even if it isn’t concurrent.

Swift Arrays, Dictionaries, Structs, and other value types are not thread-safe by default. For example, when you have multiple threads trying to access and modify the same array, you will start running into trouble.

There are different solutions to the readers-writers problem, such as using locks or semaphores. But the relevant solution I wish to discuss here is the use of an isolation queue.

Let’s say we have an array of integers, and we want to submit asynchronous work that references this array. As long as our work only reads the array and does not modify it, we are safe. But as soon as we try to modify the array in one of our asynchronous tasks, we will introduce instability in our app.

It’s a tricky problem because your app can run 10 times without issues, and then it crashes on the 11th time. One very handy tool for this situation is the Thread Sanitizer in Xcode. Enabling this option will help you identify potential race conditions in your app.

To demonstrate the problem, let’s take this (admittedly contrived) example:

One of the async tasks is modifying the array by appending values. If you try running this on your simulator, you might not crash. But run it enough times (or increase the loop frequency on line 7), and you will eventually crash. If you enable the thread sanitizer, you will get a warning every time you run the app.

To deal with this race condition, we are going to add an isolation queue that uses the barrier flag. This flag allows any outstanding tasks on the queue to finish, but blocks any further tasks from executing until the barrier task is completed.

Think of the barrier like a janitor cleaning a public restroom (shared resource.) There are multiple (concurrent) stalls inside the restroom that people can use.

Upon arrival, the janitor places a cleaning sign (barrier) blocking any newcomers from entering until the cleaning is done, but the janitor does not start cleaning until all the people inside have finished their business. Once they all leave, the janitor proceeds to clean the public restroom in isolation.

When finally done, the janitor removes the sign (barrier) so that the people who are queued up outside can finally enter.

Here’s what that looks like in code:

We have added a new isolation queue, and restricted access to the private array using a getter and setter that will place a barrier when modifying the array.

The getter needs to be sync in order to directly return a value. The setter can be async, as we don’t need to block the caller while the write is taking place.

We could have used a serial queue without a barrier to solve the race condition, but then we would lose the advantage of having concurrent read access to the array. Perhaps that makes sense in your case, you get to decide.

Thank you so much for reading this far! I hope you learned something new from this article. I will leave you with a summary and some general advice:

I like the metaphor from the Swift Concurrency Manifesto of having an ‘island of serialization in a sea of concurrency’. This sentiment was also shared in this tweet by Matt Diephouse:

When you apply concurrency with that philosophy in mind, I think it will help you achieve concurrent code that can be reasoned about without getting lost in a mess of callbacks.

If you have any questions or comments, feel free to reach out to me on Twitter

Besher Al Maleh

Cover photo by Onur K on Unsplash

http://khanlou.com/2016/04/the-GCD-handbook/

[3]
Edit
Query
Report
W.C. Spicer
Telephone Maintainer
Answer # 2 #

→ Concurrency : Concurrency is nothing but doing multiple jobs at the same time. It helps to write efficient, fast and super responsive code in a way doing more than one thing at a time. At the same time if we mis-used it, we will be tortured without having any mercy.

For Newbies or junior developers it might be a bit hard to achieve concurrency but once we learn it, it is easy to accomplish.

What is Concurrency?

In Computer Science, concurrency refers to the ability of different parts or units of a program, algorithm, or problem to be executed out-of-order or in partial order, without affecting the final outcome. This allows for parallel execution of the concurrent units, which can significantly improve overall speed of the execution in multi-processor and multi-core systems. In more technical terms, concurrency refers to the decomposability property of a program, algorithm, or problem into order-independent or partially-ordered components or units(wikipedia).

in Simple terms: Concurrency means that your application executes multiple streams (or threads) of operations all at the same time. This way the user interface stays responsive as you are performing your work other than main thread(main thread used to update the UI). UI will be refreshed for every 60 milli seconds, so it has high priority and should execute on main thread.

in iOS, we can achieve concurrency in two ways.

Operations are built on top of the GCD, so it is higher abstraction and easy to use. however in some cases we need to dig into the GCD, to avail of the benefits of it in order to get efficient way of working concurrency.

we will see how to work both of these in this tutorial.

Before Concurrency Code Snippet:

I used Image urls from https://unsplash.com/. With the following code, you will get your UI Freeze while scrolling the tableview because images are fetching on main thread. Main thread should always be free for UI activities. we solve this while going through forward in this tutorial.

→ Why do we need Concurrency :

Assume you are calling any web-service or doing some heavy work like getting an image from remote, applying some filters to that image before displaying it. If we do all these work on the main thread our APP’s UI will be freeze(Like the above example) to make it unresponsive to the users.

Users can not interact with the UI, so it gives bad experience. If we do all these things with the help of background threads, UI does not freeze on main thread, So user still can interact with our APP’s User Interface

2. Execution of Demanding Tasks in less time:

Executing high CPU demanding tasks on the background, APP’s UI is responsive and gives smoother experience to the users.

3. Utilise Device Hardware :

All iOS Devices are multi core processors that allows developers to execute multiple tasks parallel. If we not use it, it means we are wasting the provided resources.

While working on concurrency we face three problems, known as

The system provided APIs take care of the first two, we need to take care of the last one by using the APIs properly.

→ Task : A simple single piece of work that needs to be done.

→ Process : A process is an executable chunk of code that is made up of multiple threads. Simply it is nothing but a running program like your app, opening a safari pages, etc.

→ Thread : A concurrently running mechanism that executes multiple sets of instructions(Processes)

It contains a list of instructions (your code in Assembly format) and sits there on your disk until the user wishes to run it. The OS will then load that process into memory, start an instruction pointer that tells us which instruction of the program is currently being executed, and have the CPU sequentially execute its instructions until they end, terminating the process.Each process gets its own section of physical memory dedicated to itself. They do not share these addresses with other processes.

If a system has only one core, the system will switch between the threads that are currently executing(Using Time Multiplex Scheduling System), this is also called as ‘Context Switching’. if there are more than one core, the threads will be distributed among those cores for processing. As technology improves the hardware and computing powers gets better now. So we should take the advantages of these.

→ Grand Central Dispatch :

A Dispatch Queue will automatically create and reuse a pool of threads as it finds necessary, abstracting from you the hassle of spawning threads manually and dealing with potential issues of doing so.

We have two types of Dispatch Queues :

These two types execute the code in the other threads than the thread they were created on. Simply, These queues dispatch your blocks of code to other threads.

→ Serial Queues:

Serial Queues execute one task at a time. These are good to use when those tasks are required to use system resources. The tasks in one serial queue do not care about the other tasks in a separate serial queue.

We can use multiple serial queues so they can run parallel.

Example: A real world humans queue before a cinema, where one is served at a time. If every one(Thread) in the queue comes at a time to get the tickets it will be messy and the person at the counter(System Resource) does not know whom to serve and gets confused.

Advantages :

→ Concurrent Queues:

As the name suggests, Concurrent Queues allow to execute multiple tasks in parallel at the same time. The tasks start in the order in which they are added in the queue, but their execution will occur concurrently. These tasks do not have to wait each other to start.

In the concurrent queues, it is guaranty that the tasks start in the order, but we don’t know the order of execution, execution time or number of currently executing tasks at a time.

In a real world example, it is like human queues in a cinema booking counter, with more than one queue with multiple service counters. If there are 5 counters and five queues, 5 persons will be served at a time.

[2]
Edit
Query
Report
Pasha Dimitri
TEST ENGINEER MECHANICAL EQUIPMENT
Answer # 3 #

For an app, performance is very important and as an iPhone user, no one wants to wait to see a response after taking an action, if it happens then it can cost you a negative rating for your app.

We can optimize the performance of an app in so many ways, concurrency is one of them

What is Concurrency?

If more than one task is happening at the same time then it’s called concurrency. Since 2011 all iPhone supports multiple tasks to execute at the same time. We can run multiple tasks at the same time by adding some code.

this article is all about improving the performance of the app, we can run code concurrently mainly by two ways in iOS -

Let’s deep dive into it -

Grand Central Dispatch(GCD)

Dispatch, also known as Grand Central Dispatch (GCD), contains language features, runtime libraries, and system enhancements that provide systemic, comprehensive improvements to the support for concurrent code execution on multicore hardware in macOS, iOS, watchOS, and tvOS.The BSD subsystem, Core Foundation, and Cocoa APIs have all been extended to use these enhancements to help both the system and your application to run faster, more efficiently, and with improved responsiveness. Consider how difficult it is for a single application to use multiple cores effectively, let alone to do it on different computers with different numbers of computing cores or in an environment with multiple applications competing for those cores. GCD, operating at the system level, can better accommodate the needs of all running applications, matching them to the available system resources in a balanced fashion. — Apple documentation.

Serial and concurrent queues

Serial queues only have a single thread associated with them and thus allows only a single task to be executed at any given point of time.

A concurrent queue can utilize as many threads as the system has resources for. A thread will be created and released as necessary in the concurrent queue.

Synchronous and asynchronous tasks

If we have some tasks pending in queue either we can complete it synchronously or asynchronously.

When a task is running synchronously, the current queue will be blocked until the task is not completed before moving to another task.

On the other hand, asynchronous task on the current queue will start, but return control to the current queue immediately. This way the current queue will not be blocked and we can perform other tasks on the current queue.

We can finish the task faster by running it concurrently instead of serial.

Dispatch Queues

when you create a queue, the OS will assign one or more threads to the queue. If an existing thread is available, they can be reused; if not, then the OS will create them accordingly. Creating a dispatch queue -

That’s it, a serial queue has been created. :)

when the app starts by default a main dispatch queue will be created for you. it’s a serial queue and its mainly responsible for UI. You can access it like -

You should not execute any synchronous task here unless it is not related to UI. Otherwise, it will freeze your UI until the execution of the synchronous task.

You can create a concurrent queue by passing attribute as .concurrent. Like this -

Concurrent queues are divided into six different global concurrent queues categories, depending on the Quality of service (QoS).

Quality of service

You can prioritize your task using QoS. Higher priority tasks should perform faster compared to the lower priority task because it takes more system resources. If you want a concurrent queue and don’t want to manage it on your own then use pre-defined global queues.

Here are different Quality of services -

.default and .unspecified are rarely used Qos.

You can create your own concurrent dispatch queue with Qos like this -

You can add a task to queues like this -

Always update UI inside main queue. If you want to use the sync method then use it carefully here otherwise it can cause you a deadlock situation. Never call sync method from main thread it will block main thread and cause you deadlock.

When you are implementing concurrency in your app, you should be careful otherwise you can face concurrency problems like Race conditions, deadlock, or priority inversion.

Sometimes you need to execute multiple tasks at the same time but it doesn’t matter which task is finishing first, you just want to know when all tasks are finish. For this scenario, we can use DispatchGroup. Here is a basic implementation of DispatchGroup.

In some cases, you want to specify how many threads have access to a shared resource in this situation we can use a semaphore.

[1]
Edit
Query
Report
Answer # 4 #

You can perform multiple tasks or threads in a concurrent manner on the iOS operating system. This process is called concurrency. With iOS, you can implement and manage concurrency in your applications in a variety of ways. These approaches are GCD (Grand Central Dispatch), NSOperationQueue, NSThread, and more.

[1]
Edit
Query
Report
Justine Chaffey
Scenic Artist
Answer # 5 #
  • Using Operations and OperationQueues.
  • Using Grand Central Dispatch(GCD).
[0]
Edit
Query
Report
T.S. Shailaj
VACUUM BOTTLE ASSEMBLER