How does the queue queue / private dispatch queue know when a task is completed?

(Perhaps answered How does the next send queue guarantee resource protection? But I don’t understand how)

Question

How does gcd know when an asynchronous task (such as a network task) is completed? Should I use dispatch_retain and dispatch_release for this purpose? Update: I cannot call any of these methods using ARC ... What should I do?

More details

I interact with a third-party library that does a lot of network access. I created the shell through a small class that basically offers all the methods that I need from a third-party class, but transfers the calls to dispatch_async(serialQueue) { () -> Void in (where serialQueue is a member of my wrapper class).

I am trying to ensure that every call to the base library ends before the next one begins (one way or another that is not yet implemented in the library).

+5
source share
3 answers

Serialization of a job in a sequential send queue is in the unit of work that is directly sent to the queue. After execution reaches the end of the presented closure (or it returns), you can execute the next block of work in the queue.

It is important to note that any other asynchronous tasks that may have been started at the close may still work (or perhaps not even started), but they are not considered.

For example, for the following code:

 dispatch_async(serialQueue) { print("Start") dispatch_async(backgroundQueue) { functionThatTakes10Seconds() print("10 seconds later") } print("Done 1st") } dispatch_async(serialQueue) { print("Start") dispatch_async(backgroundQueue) { functionThatTakes10Seconds() print("10 seconds later") } print("Done 2nd") } 

The result will be something like this:

Start

Done 1st

Start

Done 2nd

After 10 seconds

After 10 seconds

Please note that the first 10 second task was not completed before sending the second sequential task. Now compare:

 dispatch_async(serialQueue) { print("Start") dispatch_sync(backgroundQueue) { functionThatTakes10Seconds() print("10 seconds later") } print("Done 1st") } dispatch_async(serialQueue) { print("Start") dispatch_sync(backgroundQueue) { functionThatTakes10Seconds() print("10 seconds later") } print("Done 2nd") } 

The result will be something like this:

Start

After 10 seconds

Done 1st

Start

After 10 seconds

Done 2nd

Please note that this time, because the 10-second task was sent synchronously, the sequential queue was blocked, and the second task did not start until the first was completed.

In your case, there is a very good chance that the operations you certify will be sent by the asynchronous tasks themselves (since this is the nature of network operations), so the next dispatch queue itself is not enough.

You can use DispatchGroup to block the sequential send queue.

 dispatch_async(serialQueue) { let dg = dispatch_group_create() dispatch_group_enter(dg) print("Start") dispatch_async(backgroundQueue) { functionThatTakes10Seconds() print("10 seconds later") dispatch_group_leave(dg) } dispatch_group_wait(dg) print("Done") } 

This will lead to the conclusion

Start

After 10 seconds

Done

dg.wait() blocks the sequential queue until the number of calls to dg.leave matches the number of calls to dg.enter . If you use this method, you need to be careful that all possible completion paths for your wrapped invocation of the dg.leave operation. There are also options on dg.wait() that accept a timeout parameter.

+8
source

As mentioned earlier, DispatchGroup is a very good mechanism for this.

You can use it for synchronous tasks:

 let group = DispatchGroup() DispatchQueue.global().async(group: group) { syncTask() } group.notify(queue: .main) { // done } 

It is better to use notify than wait , since wait blocks the current thread, so it is safe for non-main threads.

You can also use it to perform asynchronous tasks:

 let group = DispatchGroup() group.enter() asyncTask { group.leave() } group.notify(queue: .main) { // done } 

Or you can even perform any number of parallel tasks of any synchronization:

 let group = DispatchGroup() group.enter() asyncTask1 { group.leave() } group.enter() //other way of doing a task with synchronous API DispatchQueue.global().async { syncTask1() group.leave() } group.enter() asyncTask2 { group.leave() } DispatchQueue.global().async(group: group) { syncTask2() } group.notify(queue: .main) { // runs when all tasks are done } 

It is important to note a few things.

  • Always check if your asynchronous functions refer to a completion callback, sometimes third-party libraries or cases when your self is weak and forget about it, and no one bothered to check if the body turned out when self nil . If you do not verify this, you may hang up and never receive a notification callback.
  • Remember to make all the necessary calls to group.enter() and group.async(group: group) before calling group.notify . Otherwise, you can get a race condition, and the group.notify block can fire before you really finish your tasks.

FIFTH EXAMPLE

 let group = DispatchGroup() DispatchQueue.global().async { group.enter() syncTask1() group.leave() } group.notify(queue: .main) { // Can run before syncTask1 completes - DON'T DO THIS } 
+1
source

The answer to the question in your body of questions:

I am trying to ensure that every call to the main library ends before the next start.

A sequential queue ensures that tasks are completed in the order in which you add them to the queue.


I really don't understand the question in the header, but:

How does the sequence of queues ... know when the task is completed?

0
source

Source: https://habr.com/ru/post/1264034/


All Articles