In fact, I don’t know how to write this title, it is very broken, and I didn’t think of particularly suitable examples can all be put together. Just lay it out like that.

1. Dispatch_once and Swift singletons

Using the dispatch_once function ensures that a piece of code is executed only once during program execution. So normally in the OC era, we would use it to write singletons.

But, but, but: this function has been removed since Swift3.0. Yeah, it was deleted. No, no.

Swift has been supporting thread-safe global lazy initialization and static attributes with dispatch_one since Swift 1.x. Static var dispatch_once = dispatch_once = dispatch_once = dispatch_once;

How about Swift singleton? In fact, there are many methods, there are OC heart Swift skin writing method, new bottles of old wine writing method, that since we started Swift, put behind the past that write heavy burden. The atypical techie here shares only one of them.

final class SingleTon: NSObject {
    static let shared = SingleTon()
    private override init() {}}Copy the code

What? Are you doing something? Is that all? Yes, because it is a global variable, it will only be created once.

  • usefinalTo terminate inheritance of the singleton class.
  • Make the initialization method private to prevent external objects from creating instances of the singleton class by accessing the init method.

2. dispatch_after

In GCD we use the dispatch_after() function to delay the execution of tasks in the queue. When the specified time is up, a new thread is started and the task in the queue is executed immediately.

So dispatch_after does not block the current task, it does not add the task to the thread and wait for the time to execute it. Instead, it waits for the time to add it to the thread.

Let’s look at it in two time formats.

Method 1: Use DispatchTime

@IBAction func delayProcessDispatchTime(_ sender: Any) {//dispatch_time calculates the relative time when the device is asleep. //Creates a 'DispatchTime' relative to the system clock that ticks since bootlet time = DispatchTimeInterval.seconds(3)
    let delayTime: DispatchTime = DispatchTime.now() + time
    DispatchQueue.global().asyncAfter(deadline: delayTime) {
        Thread.current.name = "dispatch_time_Thread"
        print("Thread Name: \(String(describing: Thread.current.name))\n dispatch_time: Deplay \(time) seconds.\n")}}Copy the code

Method 2: Use absolute time, DispatchWallTime

@ IBAction func delayProcessDispatchWallTime (_ sender: Any) {/ / dispatch_walltime is used to calculate the absolute time.letDelaytimeInterval = Date().timeIntervalsincenow + 2.0let nowTimespec = timespec(tv_sec: __darwin_time_t(delaytimeInterval), tv_nsec: 0)
    letDelayWalltime = DispatchWallTime(timespec: nowTimespec) Timespec structure is required to create DispatchWallTime type. DispatchQueue.global().asyncAfter(wallDeadline: delayWalltime) { Thread.current.name ="dispatch_Wall_time_Thread"
        print("Thread Name: \(String(describing: Thread.current.name))\n dispatchWalltime: Deplay \(delaytimeInterval) seconds.\n")}}Copy the code

3. Loop, suspend, and resume queues

3.1 dispatch_apply

The dispatch_apply function is used to loop through tasks in the queue. Swift 3.0 has made some optimizations for this, using the following method:

public class func concurrentPerform(iterations: Int, execute work: (Int) -> Swift.Void)
Copy the code

Loops are designed to save time, so parallel queues are used by default. Let’s try using this upgraded version of Dispatch_apply to perform 10 print tasks.

@IBAction func useDispatchApply(_ sender: Any) {

        print("Begin to start a DispatchApply")
        DispatchQueue.concurrentPerform(iterations: 10) { (index) in
            
            print("Iteration times:\(index),Thread = \(Thread.current)")}print("Iteration have completed.")}Copy the code

The running results are as follows:

See, are all the tasks done in parallel? It’s highlighted in red, it’s not a typical tech geek and I want to remind you that there are still some tasks that are going on in the main thread. As it loops through tasks in parallel queues, it starts a new thread, though it may execute some tasks in the current thread.

In our last article, we said that if there are particularly time-consuming operations in the tasks that need to be looping, they should be placed in global. How to avoid this operation on the main thread?

Here, give me three seconds to think. Do you see when you call this method that it’s written in the UI thread? Start a gloablQueue and let it do the work. BINGO! My dear fellow, you have learned the truth so well that you can come to my back garden after school. Hei hei ✧(≖ plus-one item ≖✿) Hei hei

3.2 Queue suspension and wake up

If a bunch of tasks are being executed, all of a sudden the next task doesn’t want to be executed. What then? We can let them hang for a while, and then we can get them up and running.

Suspending does not suspend a queue that is currently executing, but only a queue that is not yet executing.

@IBAction func useDispatchSuspend(_ sender: Any) {
    let queue = DispatchQueue(label: "new thread"Suspend queue.suspend() queue.async {print("The queue is suspended. Now it has completed.\n The queue is \"\(queue.label)\". ")}print("The thread will sleep for 3 seconds' time") DispatchQueue.main.asyncAfter(deadline: DispatchTime. Now () + DispatchTimeInterval. Seconds (3)) {/ / wake, begin to execute the queue. The resume ()}}Copy the code

We can also look at the printout of the control bar. You can obviously see that the code is not executed in order, and the printing in the new queue is only executed after being woken up.

4. Semaphore

The semaphore was used in an example in a previous article, when someone asked me specifically what a semaphore was. Now we can talk more about this.

Don’t ask me which example I used it in, I can’t remember, but I remember someone asked me about Semaphore.

Sometimes multiple threads working on the same data can have unexpected effects. Who knows how many people operate on the same data at the same time?

To ensure that only one thread is modifying the data at a time, we need to use the semaphore at this point. When the semaphore is zero, other threads must wait to modify or use the data. DispatchTime distantFuture, wait for so long. It means to wait and wait… OC calls this DISPATCH_TIME_FOREVER.

If the semaphore is set to 0, it means that no one can use the resource. So, be sure to set the semaphore to non-zero when you run out!

// Create a semaphore with an initial value of 1letSemaphoreSignal = DispatchSemaphore(value: 1) semaphoresignal.wait ()Copy the code

4.1 Simple and practical

We simply print the globalQueue in order 1->5, with a 1 second break at a time.

@IBAction func useSemaphore(_ sender: Any) {
    let semaphoreSignal = DispatchSemaphore(value: 1)
    
    for index in1... 5 { DispatchQueue.global().async { semaphoreSignal.wait()print(Thread.current)
            print("This is the \(index) execution.\n")
            semaphoreSignal.signal()
        }
        print("Test print")}}Copy the code

Take a look at the print:

What would a globalQueue normally print without a semaphore? If you don’t remember, see the previous article. IOS Multithreading series 3: Using GCD to download images asynchronously.

Have you ever wondered what it would look like to create a semaphore with an initial value of 2 or a larger number, such as 50? Try it yourself. Think about it.

4.2 Task coordination among multiple threads

In practice, many times we need to coordinate among multiple tasks, each of which is multi-threaded.

For example, we download music, album covers backstage. Wait for both to complete before notifying the user that they can listen to music. Both tasks are multi-threaded and we don’t really know when they will be finished. At this point, we can rely on the semaphore to make everyone wait for each other.

To simplify the process, the example simulates an operation that would take 1 second in another method. Perform subsequent operations only when the operation is complete.

func semaphoreDemo() -> Void {
    let sema = DispatchSemaphore.init(value: 0)
    getListData { (result) in
        if result == true {
            sema.signal()
        }
    }
    sema.wait()
    print("I can finally get to work.")
}

private func getListData(isFinish:@escaping (Bool) -> ()) {
    
    DispatchQueue.global().async {
        Thread.sleep(forTimeInterval: 1)
        print("global queue has completed!")
        isFinish(true)}}Copy the code

This example can also be done with group. ! Is the dalai. Can also.

5. Task group

GCD task groups are often used in development, when some action needs to be performed after a set of tasks.

The responsibility of the DispatchGroup is to send a notification when all the tasks in the queue have finished.

Since it is a group, there must be many queues in it, otherwise it can not be called a group.

There are two methods for associating queues and groups: manual and automatic.

5.1 Automatic Association

I must start with automatic, because automatic is usually the easiest. You have to ask.

@IBAction func useGroupQueue(_ sender: UIButton) {
    letGroup = DispatchGroup() // Simulation loop creates several global queuesfor index in0... Dispatchqueue.global (). Async (group: group, execute: dispatchworkItem.init (block: {thread.sleep ())forTimeInterval: TimeInterval(arc4random_uniform(2) + 1))
            print("Task \(index) completed"Group. Notify (queue: dispatchqueue.main) {print("The task group has completed its tasks!")}print("Print it and test it.")}Copy the code

Look at the print:

5.2 Manual Association

Next we will manually manage the relationship between task groups and queues.

Enter () and leave() are a pair. The former indicates entering the task group. The latter indicates leaving the task group.

letManualGroup = DispatchGroup() // Simulation loop establishes several global queuesfor manualIndex in0... 3 {// enter the queue manualgroup.enter () dispatchqueue.global ().async {// let the Thread rest randomly for a few seconds thread.sleep ()forTimeInterval: TimeInterval(arc4random_uniform(2) + 1))
        print("----- manual task \(manualIndex) completed"Manualgroup.leave ()}} // Send notification manualgroup.notify (queue: dispatchqueue.main) {print("The manual task group has completed its tasks!")}Copy the code

There are many scenarios that can be accomplished with task groups. For example, the UI is refreshed after multiple tasks are completed. Just put the UI refresh in Notify.

Remember which queue to refresh the UI? hoho~

Finally, all the code is here: download it from gitHub and give it a Star ~(~ o ~ 3 ~) ~ love you