[Swift] About asynchronous processing "Operation"

Operation, OperationQueue class

I posted a GCD article about asynchronous processing before, GCD is implemented in the core library libdispatch.

Similarly, the core library Foundation is also a class that realizes asynchronous processing. There are Operation class and OperationQueue class.

The Operation class is an encapsulation of the task to be executed and its information.

** Instances of this Operation class are queued and executed sequentially. At this time, the OperationQueue class plays the role of a queue. ** **

Execution method

Task definition

At the time of GCD, the task was written in the closure, When using Operation, define it as a subclass of Operation class.

Representing it as a class is a little troublesome, but it also has advantages. It can provide an easy-to-use interface.

The processing content is described in the main () method.


import Foundation
import PlaygroundSupport

PlaygroundPage.current.needsIndefiniteExecution = true

class SomeOperation: Operation {
    let value: Int
    init(value: Int) {
        self.value = value
    }
    
    override func main() {
        //Stop the thread for 1 second
        Thread.sleep(forTimeInterval: 1)
        print(value)
    }
}

Now that you have defined the task, Then you need to queue this task.

But you have to create a queue before you can queue it.

Queue generation

Create an instance of ** OperationQueue class ** that will be the queue to execute the task.

The OperationQueue class can be instantiated with a no-argument initializer. After instantiation, make various settings via properties.


import Fountation

let queue = OperationQueue()

** · name property ** You can give the queue a name by setting the name property. It is common to use ** reverse DNS format ** for this value.

** · maxConcurrentOperationCount property ** The number of tasks that a particular queue can run at the same time is well determined by system conditions, You can specify that number by using the maxConcurrentOperationCount property.

Obviously, setting a large number does not mean that it will be faster. Please note that it may be rather slow.

** ・ QualityOfService type ** GCD was able to determine execution priority by QoS, This is the OperationQueue class version of it.

The role of a value of type QualityOfService is similar to QoS in GCD.


import Foundation

let queue = OperationQueue()
queue.name = "com.example.my_operation_queue"
queue.maxConcurrentOperationCount = 2
queue.qualityOfService = .userInitiated

Now that we have created cues and tasks You need to add a task to the queue.

Add a task to the queue

To add a task, use the addOperation (_ :) method of the OperationQueue class. Pass the value of Operation class as an argument.

Also, the OperationQueue class is a method for passing multiple Operation classes, addOperations (_: waitUntilFinished :) is also available.

The type of the first argument is [Operation] type.

In the following sample code Five SampleOperation type values ​​are created and added to the queue.

If true is given to the second argument waitUntilFinished, ** Block the calling thread until all tasks have been executed. ** **

Since false is given this time, the next process is executed as it is without waiting for the task to finish.


import Foundation
import PlaygroundSupport

PlaygroundPage.current.needsIndefiniteExecution = true

class SampleOperation: Operation {
    let value: Int
    init(value: Int) {
        self.value = value
    }
    
    override func main() {
        Thread.sleep(forTimeInterval: 1)
        print("value: \(value)")
    }
}

let queue = OperationQueue()
queue.name = "com.example.my_operation_queue"
queue.maxConcurrentOperationCount = 2
queue.qualityOfService = .userInitiated

var operations: [SampleOperation] = []

for i in 0..<5 {
    operations.append(SampleOperation(value: i))
}

queue.addOperations(operations, waitUntilFinished: false)
print("A task has been added to the Operation Queue.")

Execution result
A task has been added to the Operation Queue.
value: 0
value: 1
value: 2
value: 3
value: 4

Because it is specified as maxConcurrentOperationCount = 2 Execute two tasks every second.

If you comment out Thread.sleep (forTimeInterval: 1) The execution result will be as follows.


import Foundation
import PlaygroundSupport

PlaygroundPage.current.needsIndefiniteExecution = true

class SampleOperation: Operation {
    let value: Int
    init(value: Int) {
        self.value = value
    }
    
    override func main() {
//        Thread.sleep(forTimeInterval: 1)
        print("value: \(value)")
    }
}

let queue = OperationQueue()
queue.name = "com.example.my_operation_queue"
queue.maxConcurrentOperationCount = 2
queue.qualityOfService = .userInitiated

var operations: [SampleOperation] = []

for i in 0..<5 {
    operations.append(SampleOperation(value: i))
}

queue.addOperations(operations, waitUntilFinished: false)
print("A task has been added to the Operation Queue.")

Execution result
value: 0
value: 1
A task has been added to the Operation Queue.
value: 2
value: 3
value: 4

Cancel task

The Operation class has a cancel () method that has the ability to cancel a task.

The following sample code adds the cancelel () method to the previous code. You can see that value: 2 does not exist by operations [2] .cancel ().


import Foundation
import PlaygroundSupport

PlaygroundPage.current.needsIndefiniteExecution = true

class SampleOperation: Operation {
    let value: Int
    init(value: Int) {
        self.value = value
    }
    
    override func main() {
        print("value: \(value)")
    }
}

let queue = OperationQueue()
queue.name = "com.example.my_operation_queue"
queue.maxConcurrentOperationCount = 2
queue.qualityOfService = .userInitiated

var operations: [SampleOperation] = []

for i in 0..<5 {
    operations.append(SampleOperation(value: i))
}

queue.addOperations(operations, waitUntilFinished: false)
operations[2].cancel()

Execution result
value: 0
value: 1
value: 3
value: 4

Task dependency settings

** The Operation class allows you to set dependencies between multiple tasks. ** **

By defining dependencies Only after a task has finished will this task be executed! You can do such processing.

To specify a task that should be executed before that, Use the addDependency (_ :) method of the Operation class.

In the argument, enter the task to be executed first.


import Foundation
import PlaygroundSupport

PlaygroundPage.current.needsIndefiniteExecution = true

class FirstOperation: Operation {
    override func main() {
        print("This is FirstOperation")
    }
}

class SecendOperation: Operation {
    override func main() {
        print("This is SecondOperation")
    }
}


let queue = OperationQueue()
queue.name = "com.example.my_operation_queue"
queue.maxConcurrentOperationCount = 1
queue.qualityOfService = .userInteractive

var operations: [Operation] = []

operations.append(FirstOperation())
operations.append(SecendOperation())

operations[0].addDependency(operations[1])
queue.addOperations(operations, waitUntilFinished: false)

Execution result
This is SecondOperation
This is FirstOperation

FirstOperation () was supposed to be executed first, In the part of operations [0] .addDependency (operations [1]) It was executed from SecondOperation () because it defines a dependency.

Timing to use

Implement multiple asynchronous processes

Operation, OperationQueue class It is an object-oriented abstraction of asynchronous processing.

Besides simple thread switching, Because it has task dependencies and cancel functions, Suitable for ** more complicated processing than GCD. ** **

Also, since the Operation and OperationQueue classes use GCD internally, ** What can be defined in GCD can also be defined in Operation and OperationQueue classes. ** **

GCD can represent tasks in closures, You can use global queues without creating a queue.

Operation and OperationQueue classes need to define tasks and generate queues, You can perform complicated processing accordingly.

** Therefore, simple asynchronous processing is defined in GCD, Let's define cancellation and dependency in Operation and OperationQueue classes! ** **

There is another Thread class for asynchronous processing, ** I will not introduce it because it seems that there is almost no opportunity to use it. ** **

If I had a chance to use it, I would like to share it at that time.

Thank you for watching until the end.

Recommended Posts

[Swift] About asynchronous processing "Operation"
[Swift] Asynchronous processing "GCD"
[Swift] What is asynchronous processing?
[Swift] Asynchronous processing using PromiseKit
[Swift] "for-in statement" about iterative processing
[Swift] About generics
About UI thread processing in Android asynchronous
[Swift] Implement swipe processing
[Swift] About enumeration types
[Swift] About protocol extension
About [Cocoa] [Swift] AirPlay 2
[Swift] [Beginner]] About range operators
About simple operation of Docker
[Swift] Summary about Bool type
Spring with Kotorin --6 Asynchronous processing
Asynchronous processing with Shoryuken + SQS
[Swift] Processing to share screenshots
[Swift] I thought about compare
Try implementing asynchronous processing in Azure
Implementation of asynchronous processing in Tomcat
About if statement and branch processing
About file copy processing in Java