GCD series I use queues

GCD queue usage

file

First look at the official documents. DispatchQueue is a class object, which inherits from DispatchObject;
DispatchObject implements OS_ Method in object, guess OS_object may be a protocol, which is not found in the document
First look at DispatchObject:

func activate() 
    Activates the dispatch object.
func resume()
    Resumes the invocation of block objects on a dispatch object.
func suspend()
    Suspends the invocation of block objects on a dispatch object.
func setTarget(queue: DispatchQueue?)
    Specifies the dispatch queue on which to perform work associated with the current object.
enum DispatchPredicate
    Logical conditions to evaluate within a given execution context.
func dispatchPrecondition(condition() -> DispatchPredicate)
    Checks a dispatch condition necessary for further execution.

That is to say, as a queue, DispatchQueue also implements the above method. In addition, it also implements the following method

class var main: DispatchQueue
    The dispatch queue associated with the main thread of the current process.
class func global(qos: DispatchQoS.QoSClass) -> DispatchQueue
    Returns the global system queue with the specified quality-of-service class.
init(label: String, qos: DispatchQoS, attributes: DispatchQueue.Attributes, autoreleaseFrequency: DispatchQueue.AutoreleaseFrequency, target: DispatchQueue?)
    Creates a new dispatch queue to which you can submit blocks.
enum DispatchQoS.QoSClass
    Quality-of-service classes that specify the priorities for executing tasks.
struct DispatchQueue.Attributes
    Attributes that define the behavior of a dispatch queue.
enum DispatchQueue.AutoreleaseFrequency
    Constants indicating the frequency with which a dispatch queue autoreleases objects.
class OS_dispatch_queue_main
    A system-provided dispatch queue that schedules tasks for serial execution on the app's main thread.
class OS_dispatch_queue_global
    A system-provided dispatch queue that schedules tasks for concurrent execution.
class OS_dispatch_queue_serial
    A custom dispatch queue that schedules tasks for serial execution on an arbitrary thread.
class OS_dispatch_queue_concurrent
    A custom dispatch queue that schedules tasks for concurrent execution.

According to the document:

  • DispatchQueue is a first in first out queue. When users submit tasks to the queue, the queue will schedule the tasks to the threads in the thread pool for execution
  • Tasks submitted by users can be executed synchronously or asynchronously, serially or asynchronously. Tasks in mainQueue can only be executed serially

Document reminder:
When using queues, you need to avoid creating too many threads:

  • Avoid the execution of block tasks when executing tasks in the concurrent queue, because the scheduling system will continue to create threads in this scenario
  • Avoid creating your own queue, because creating your own queue will consume thread resources. You should point to globalQueue by setting targetQueue to avoid creating too many threads

practice

Create different queues

  • Main queue: dispatchqueue main
  • Global queue: dispatchqueue global()
  • Self built serial queue: dispatchqueue init(label: “test.serial.queue”)
  • Self built concurrent queue: dispatchqueue init(label: “test.serial.queue”, attributes: .concurrent)
import Foundation

func createQueue() {
    ///Main line
    let mainQueue = DispatchQueue.main
    print(mainQueue) // <OS_dispatch_queue_main: com.apple.main-thread>

    /*
    class func global(qos: DispatchQoS.QoSClass = .default) -> DispatchQueue
    The default priority is default, and globalQueue is a concurrent queue
    */
    let globalQueue = DispatchQueue.global()
    print(globalQueue) // <OS_dispatch_queue_global: com.apple.root.default-qos>

    ///Create custom queue, serial
    let customSerialQueue = DispatchQueue.init(label: "test.serial.queue")
    print(customSerialQueue)
    /*
     Create a concurrent queue. No priority is specified at this time. The priority is
     You can specify whether it is concurrent, not the default serial. In addition, you can specify that the new queue is inactive
     static let concurrent: 
        DispatchQueue.Attributes The queue schedules tasks concurrently.
     static let initiallyInactive: DispatchQueue.Attributes
        The newly created queue is inactive.
    */
    let customCurrentQueue = DispatchQueue.init(label: "test.serial.queue", attributes: .concurrent)
    print(customCurrentQueue)
}


createQueue()

Use home queue

The main queue is mainly used when the task needs to be executed by the main thread
Asynchronous tasks can be executed concurrently on the main thread
Synchronizing sync in the main thread to execute the tasks of the main queue will lead to deadlock

import Foundation
/*
result:
useMainQueue start
useMainQueue end
useMainQueue task excuting
*/
func useMainQueue1() {
    ///Main line
    print("useMainQueue start")
    let mainQueue = DispatchQueue.main
    mainQueue.async {
        print("useMainQueue task excuting")
    }
    print("useMainQueue end")
}
// useMainQueue1()

/*
result:
useMainQueue start
useMainQueue end
useMainQueue2 start
[2]    54143 illegal hardware instruction  ./task
*/
func useMainQueue2() {
    ///Main line
    print("useMainQueue2 start")
    let mainQueue = DispatchQueue.main
    mainQueue.sync {
        print("useMainQueue2 task excuting")
    }
    print("useMainQueue2 end")
}
// useMainQueue2()

/*
result:
useMainQueue3 start
useMainQueue3 task excuting
useMainQueue3 end
*/
func useMainQueue3() {
    ///Main line
    print("useMainQueue3 start")
    let mainQueue = DispatchQueue.main
    mainQueue.sync {
        print("useMainQueue3 task excuting")
    }
    print("useMainQueue3 end")
}
DispatchQueue.global().async {
    useMainQueue3()
}

RunLoop.main.run()

Use global queue

    @objc func test() {
        print("\n\n")
        
        DispatchQueue.global(qos: .default).async {
            print("default", Thread.current)
        }
        DispatchQueue.global(qos: .background).async {
            print("background", Thread.current)
        }
        DispatchQueue.global(qos: .unspecified).async {
            print("unspecified", Thread.current)
        }
        DispatchQueue.global(qos: .userInitiated).async {
            print("userInitiated", Thread.current)
        }
        DispatchQueue.global(qos: .utility).async {
            print("utility", Thread.current)
        }
        DispatchQueue.global(qos: .userInteractive).async {
            print("userInteractive", Thread.current)
        }
    }


result

default <NSThread: 0x600003255400>{number = 4, name = (null)}
utility <NSThread: 0x6000032483c0>{number = 7, name = (null)}
unspecified <NSThread: 0x60000323a3c0>{number = 8, name = (null)}
userInteractive <NSThread: 0x60000322f000>{number = 9, name = (null)}
userInitiated <NSThread: 0x6000032416c0>{number = 10, name = (null)}
background <NSThread: 0x6000032483c0>{number = 7, name = (null)}



default <NSThread: 0x6000032416c0>{number = 10, name = (null)}
userInteractive <NSThread: 0x600003252140>{number = 12, name = (null)}
unspecified <NSThread: 0x60000322ec40>{number = 11, name = (null)}
userInitiated <NSThread: 0x6000032416c0>{number = 10, name = (null)}
utility <NSThread: 0x6000032416c0>{number = 10, name = (null)}
background <NSThread: 0x6000032416c0>{number = 10, name = (null)}



default <NSThread: 0x6000032416c0>{number = 10, name = (null)}
unspecified <NSThread: 0x600003232f80>{number = 14, name = (null)}
userInteractive <NSThread: 0x60000327c380>{number = 13, name = (null)}
userInitiated <NSThread: 0x6000032416c0>{number = 10, name = (null)}
utility <NSThread: 0x60000327c380>{number = 13, name = (null)}
background <NSThread: 0x6000032416c0>{number = 10, name = (null)}



default <NSThread: 0x60000327c380>{number = 13, name = (null)}
unspecified <NSThread: 0x600003232f80>{number = 14, name = (null)}
userInteractive <NSThread: 0x600003241ec0>{number = 15, name = (null)}
userInitiated <NSThread: 0x600003227fc0>{number = 16, name = (null)}
utility <NSThread: 0x600003227fc0>{number = 16, name = (null)}
background <NSThread: 0x6000032416c0>{number = 10, name = (null)}



default <NSThread: 0x600003227fc0>{number = 16, name = (null)}
userInteractive <NSThread: 0x600003241ec0>{number = 15, name = (null)}
unspecified <NSThread: 0x600003232f80>{number = 14, name = (null)}
userInitiated <NSThread: 0x60000327c380>{number = 13, name = (null)}
utility <NSThread: 0x6000032416c0>{number = 10, name = (null)}
background <NSThread: 0x6000032416c0>{number = 10, name = (null)}



default <NSThread: 0x6000032416c0>{number = 10, name = (null)}
unspecified <NSThread: 0x60000327c380>{number = 13, name = (null)}
userInteractive <NSThread: 0x600003241ec0>{number = 15, name = (null)}
userInitiated <NSThread: 0x600003227fc0>{number = 16, name = (null)}
utility <NSThread: 0x600003232f80>{number = 14, name = (null)}
background <NSThread: 0x60000327c380>{number = 13, name = (null)}



default <NSThread: 0x600003232f80>{number = 14, name = (null)}
unspecified <NSThread: 0x600003227fc0>{number = 16, name = (null)}
userInteractive <NSThread: 0x600003241ec0>{number = 15, name = (null)}
userInitiated <NSThread: 0x6000032416c0>{number = 10, name = (null)}
utility <NSThread: 0x60000327c380>{number = 13, name = (null)}
background <NSThread: 0x600003241ec0>{number = 15, name = (null)}



default <NSThread: 0x60000327c380>{number = 13, name = (null)}
unspecified <NSThread: 0x6000032416c0>{number = 10, name = (null)}
userInteractive <NSThread: 0x600003227fc0>{number = 16, name = (null)}
userInitiated <NSThread: 0x600003232f80>{number = 14, name = (null)}
utility <NSThread: 0x600003241ec0>{number = 15, name = (null)}
background <NSThread: 0x600003227fc0>{number = 16, name = (null)}

The old version can specify 4 priorities

    @objc func test() {
        print("\n\n")
        /// high > default > low > background
        /// 'global(priority:)' was deprecated in iOS 8.0
        DispatchQueue.global(priority: .low).async {
            print("low", Thread.current)
        }
        
        DispatchQueue.global(priority: .default).async {
            print("default", Thread.current)
        }
        
        DispatchQueue.global(priority: .background).async {
            print("background", Thread.current)
        }
        
        DispatchQueue.global(priority: .high).async {
            print("high", Thread.current)
        }
    }

result

default <NSThread: 0x600002b511c0>{number = 7, name = (null)}
high <NSThread: 0x600002b33900>{number = 5, name = (null)}
low <NSThread: 0x600002b75d40>{number = 6, name = (null)}
background <NSThread: 0x600002b505c0>{number = 4, name = (null)}



default <NSThread: 0x600002b75d40>{number = 6, name = (null)}
high <NSThread: 0x600002b505c0>{number = 4, name = (null)}
low <NSThread: 0x600002b33900>{number = 5, name = (null)}
background <NSThread: 0x600002b511c0>{number = 7, name = (null)}



default <NSThread: 0x600002b511c0>{number = 7, name = (null)}
high <NSThread: 0x600002b33900>{number = 5, name = (null)}
low <NSThread: 0x600002b33900>{number = 5, name = (null)}
background <NSThread: 0x600002b505c0>{number = 4, name = (null)}



default <NSThread: 0x600002b33900>{number = 5, name = (null)}
high <NSThread: 0x600002b511c0>{number = 7, name = (null)}
low <NSThread: 0x600002b505c0>{number = 4, name = (null)}
background <NSThread: 0x600002b511c0>{number = 7, name = (null)}



default <NSThread: 0x600002b505c0>{number = 4, name = (null)}
high <NSThread: 0x600002b75d40>{number = 6, name = (null)}
low <NSThread: 0x600002b511c0>{number = 7, name = (null)}
background <NSThread: 0x600002b33900>{number = 5, name = (null)}



default <NSThread: 0x600002b511c0>{number = 7, name = (null)}
high <NSThread: 0x600002b75d40>{number = 6, name = (null)}
low <NSThread: 0x600002b33900>{number = 5, name = (null)}
background <NSThread: 0x600002b505c0>{number = 4, name = (null)}



default <NSThread: 0x600002b33900>{number = 5, name = (null)}
high <NSThread: 0x600002b511c0>{number = 7, name = (null)}
low <NSThread: 0x600002b505c0>{number = 4, name = (null)}
background <NSThread: 0x600002b75d40>{number = 6, name = (null)}



default <NSThread: 0x600002b75d40>{number = 6, name = (null)}
high <NSThread: 0x600002b505c0>{number = 4, name = (null)}
low <NSThread: 0x600002b505c0>{number = 4, name = (null)}
background <NSThread: 0x600002b511c0>{number = 7, name = (null)}



default <NSThread: 0x600002b511c0>{number = 7, name = (null)}
high <NSThread: 0x600002b33900>{number = 5, name = (null)}
low <NSThread: 0x600002b505c0>{number = 4, name = (null)}
background <NSThread: 0x600002b75d40>{number = 6, name = (null)}



default <NSThread: 0x600002b75d40>{number = 6, name = (null)}
high <NSThread: 0x600002b505c0>{number = 4, name = (null)}
low <NSThread: 0x600002b33900>{number = 5, name = (null)}
background <NSThread: 0x600002b505c0>{number = 4, name = (null)}



default <NSThread: 0x600002b505c0>{number = 4, name = (null)}
high <NSThread: 0x600002b33900>{number = 5, name = (null)}
low <NSThread: 0x600002b511c0>{number = 7, name = (null)}
background <NSThread: 0x600002b33900>{number = 5, name = (null)}



default <NSThread: 0x600002b33900>{number = 5, name = (null)}
high <NSThread: 0x600002b511c0>{number = 7, name = (null)}
low <NSThread: 0x600002b511c0>{number = 7, name = (null)}
background <NSThread: 0x600002b505c0>{number = 4, name = (null)}



default <NSThread: 0x600002b505c0>{number = 4, name = (null)}
high <NSThread: 0x600002b511c0>{number = 7, name = (null)}
low <NSThread: 0x600002b33900>{number = 5, name = (null)}
background <NSThread: 0x600002b511c0>{number = 7, name = (null)}



low <NSThread: 0x600002b511c0>{number = 7, name = (null)}
default <NSThread: 0x600002b33900>{number = 5, name = (null)}
high <NSThread: 0x600002b505c0>{number = 4, name = (null)}
background <NSThread: 0x600002b511c0>{number = 7, name = (null)}



default <NSThread: 0x600002b511c0>{number = 7, name = (null)}
high <NSThread: 0x600002b75d40>{number = 6, name = (null)}
low <NSThread: 0x600002b75d40>{number = 6, name = (null)}
background <NSThread: 0x600002b75d40>{number = 6, name = (null)}



default <NSThread: 0x600002b75d40>{number = 6, name = (null)}
low <NSThread: 0x600002b505c0>{number = 4, name = (null)}
high <NSThread: 0x600002b511c0>{number = 7, name = (null)}
background <NSThread: 0x600002b33900>{number = 5, name = (null)}



default <NSThread: 0x600002b33900>{number = 5, name = (null)}
high <NSThread: 0x600002b505c0>{number = 4, name = (null)}
low <NSThread: 0x600002b505c0>{number = 4, name = (null)}
background <NSThread: 0x600002b511c0>{number = 7, name = (null)}



default <NSThread: 0x600002b511c0>{number = 7, name = (null)}
high <NSThread: 0x600002b505c0>{number = 4, name = (null)}
low <NSThread: 0x600002b505c0>{number = 4, name = (null)}
background <NSThread: 0x600002b33900>{number = 5, name = (null)}



default <NSThread: 0x600002b33900>{number = 5, name = (null)}
high <NSThread: 0x600002b511c0>{number = 7, name = (null)}
low <NSThread: 0x600002b505c0>{number = 4, name = (null)}
background <NSThread: 0x600002b75d40>{number = 6, name = (null)}



low <NSThread: 0x600002b75d40>{number = 6, name = (null)}
default <NSThread: 0x600002b505c0>{number = 4, name = (null)}
high <NSThread: 0x600002b511c0>{number = 7, name = (null)}

Number of threads that GCD can create

According to the data, gcd is internally limited to 512 threads, plus the other four threads, 516 threads

Special note here:

The 512 mentioned is the limit of gcd. After the 512 gcd threads are opened, you can still open them with NSThread.

So the chart above

Currently it should be 512, 516 = 512(max) + main thread + js thread + web thread + uikit event thread"


https://stackoverflow.com/questions/7213845/number-of-threads-created-by-gcd

Topic analysis

  • Output results of the following topics
class ViewController: UIViewController {
    var number: Int = 0
    override func viewDidLoad() {
        super.viewDidLoad()
        
        let button = UIButton()
        button.setTitle("Test", for: .normal)
        button.setTitleColor(.red, for: .normal)
        button.sizeToFit()
        button.addTarget(self, action: #selector(test), for: .touchUpInside)
        view.addSubview(button)
        button.center = view.center
    }
    
    @objc func test() {
        while number < 5 {
            DispatchQueue.global().async {
                self.number += 1
            }
        }
        print(number)
    }
}

Theoretical results, number > = 5; The actual test is about 14 and 15

  • Output results of the following topics (replace the above test method with the following method)
    @objc func test() {
        number = 0
        for _ in 1...1000 {
            DispatchQueue.global().async {
                self.number += 1
            }
        }
        print(number)
    }

Theoretical results, 0 < = number < = 1000 > & &; Actual test results

994
996
987
993
977
996
993

  • What is the final output order of the following methods
 @objc func test() {
        let queue = DispatchQueue.init(label: "test", qos: .default, attributes: .concurrent)
        queue.async {
            print(1)
        }
        queue.async {
            print(2)
        }
        queue.sync {
            print(3)
        }
        print(0)
        queue.async {
            print(4)
        }
        queue.async {
            print(5)
        }
        queue.async {
            print(6)
        }
    }

The theoretical results show that 3 is before 0 and 0 is before 456; actual

3
0
1
2
4
6
5

  • What is the output of the following questions?
    @objc func test() {
        print("\n\n")
        let queue = DispatchQueue.init(label: "test", qos: .default, attributes: .concurrent)
        print(1)
        queue.async {
            print(2)
            queue.async {
                print(3)
            }
            Thread.sleep(forTimeInterval: 0.5)
            print(4)
        }
        Thread.sleep(forTimeInterval: 0.5)
        print(5)
    }

Theoretical results: the first output 1, 2 is before 34, and the order of 3 and 4 is uncertain; The sequence between 5 and 234 is uncertain;
Actual test: 15243 if there is no delay, 12354 if there is delay

Keywords: iOS objective-c

Added by robembra on Fri, 18 Feb 2022 20:43:26 +0200