Series of articles:

multithreading

Multithreading PThreads and NSthreads

Multithreaded GCD

Multithreaded NSOperation

Multithreading

NSOperation and NSOperationQueue

NSOperation is a set of multithreaded things packaged by Apple, unlike GCD is pure C language, this is OC. GCD is faster by comparison, but essentially NSOPeration is a encapsulation of GDC.

NSOperation relative to GCD:

NSOperation has more functions available

In NSOperationQueue, you can establish dependencies between nsOperations.

NSOperationQueue supports KVO. You can monitor whether the operation isExecuted, finished, or cancelled (isCanceld)

GCD only supports QUEUES in FIFO, while NSOperationQueue can adjust the order of queue execution

NSOperation analysis

NSOperation is an abstract class and does not have the ability to encapsulate tasks. You must use a subclass of NSOperation to encapsulate tasks in three ways:

  • NSInvocationOperation
  • NSBlockOperation
  • Custom subclasses inherit NSOperation to implement the corresponding methods internally

After an Operation is created, the start method is called to start the task, which is executed synchronously on the current thread by default. Of course, you can also cancel a task in mid-stream by calling its Cancel method.

NSOperation is apple’s version of GCD, completely object-oriented, so it’s easier to understand. You can see that NSOperation and NSOperationQueue correspond to GCD tasks and queues respectively. The steps are also easy to understand:

  1. The task to be performed is encapsulated in an NSOperation object.
  2. Add the NSOperation object to an NSOperationQueue object.
  3. The NSOperation in the NSOperationQueue is automatically removed and executed in a new thread

Then the system will automatically perform the task. For synchronous or asynchronous, serial or parallel, read on:

Add tasks

  • NSInvocationOperation: You need to pass in a method name.

OBJECTIVE-C

NSInvocationOperation * Operation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(run) object:nil]; //2. Start [operation start];Copy the code

SWIFT

In the harmonious society constructed by Swift, there is no room for such scum as NSInvocationOperation that is not type safe. “Apple said. Here’s the explanation

Note:

By default, a call to the start method does not open a new thread to perform the operation, but executes the operation synchronously in the current thread. The NSOperation will only be performed asynchronously if it is placed in an NSOperationQueue

  • NSBlockOperation
+ (id)blockOperationWithBlock:(void(^)(void))block; AddExecutionBlock :(void)addExecutionBlock:(void(^)(void))block;Copy the code

As long as the operand encapsulated by NSBlockOperation is greater than 1, the operation is executed asynchronously

OBJECTIVE-C

NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{NSLog(@)"% @", [NSThread currentThread]); }]; //2. Start task [operation start];Copy the code

SWIFT

1. Create an NSBlockOperation objectlet operation = NSBlockOperation { () -> Void inprintln(NSThread.currentThread()) } //2. Start task operation.start()Copy the code

As mentioned earlier, such tasks are executed on the current thread by default. But NSBlockOperation also has a method: addExecutionBlock, which adds multiple execution blocks to the Operation. The tasks in Operation are executed concurrently. It will execute these tasks on the main thread and on multiple threads. Note the following print:

OBJECTIVE-C

NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{NSLog(@)"% @", [NSThread currentThread]); }]; // Add multiple blocksfor (NSInteger i = 0; i < 5; i++) {
          [operation addExecutionBlock:^{
              NSLog(@"%ld time: %@", i, [NSThread currentThread]); }]; } //2. Start task [operation start];Copy the code

SWIFT

1. Create an NSBlockOperation objectlet operation = NSBlockOperation { () -> Void in
            NSLog("% @", NSThread.currentThread()) } //2. Add multiple blocksfor i in0.. <5 { operation.addExecutionBlock { () -> Voidin
                NSLog("%ld - %@", i, NSThread.currentThread()) } } //2. Start task operation.start()Copy the code

A printout

The 2015-07-28 17:50:16. 585test[17527:4095467] NSThread: 0x7FF5C9701910 >{number = 1, name = main} 2015-07-28 17:50:16.585test[17527:4095666] first - <NSThread: 0x7FF5C972CAF0 >{number = 4, name = (null)} 2015-07-28 17:50:16.585test[17527:4095665] <NSThread: 0x7FF5C961B610 >{number = 3, name = (null)} 2015-07-28 17:50:16.585test[17527:4095662] NSThread: 0x7FF5C948D310 >{number = 2, name = (null)} 2015-07-28 17:50:16.586test[17527:4095666] NSThread: 0x7FF5C972CAF0 >{number = 4, name = (null)} 2015-07-28 17:50:16.586test[17527:4095467] NSThread: 0x7FF5C9701910 >{number = 1, name = main}Copy the code

NOTE: The addExecutionBlock method must be executed before the start() method, otherwise an error is reported: ‘*** -[NSBlockOperation addExecutionBlock:]: blocks cannot be added after the operation has started executing or finished’

NOTE: You may have spotted a problem. Why do I use NSLog() instead of println() in Swift? The reason is that with print()/println(), it simply uses the concept of stream, as anyone who has studied C++ knows. It prints each character to the console one by one. This is not a problem in general use, but when multiple threads are printed simultaneously, the characters on the console are jumbled together because many println() are printed at the same time. NSLog() does not have this problem. What does it look like? You could just change the NSLog of alpha to println of alpha, and try it out. See more of the differences between NSLog() and println() here

  • The custom Operation

In addition to the two operations above, we can also customize operations. To customize Operation, you need to inherit the NSOperation class and implement its main() method, because the main() method is called internally to complete the logic when the start() method is called. So if the above two classes do not satisfy your desire, you need to customize. Whatever function you want to implement can be written in it. In addition, you need to implement various methods including cancel().

Note:

Create your own automatic release pool (because you cannot access the main thread’s automatic release pool if you are doing an asynchronous operation)

The cancellation is often detected by the -(BOOL)isCancelled method.

Create a queue

As you can see from the above, we can call the start() method of an NSOperation object to start the task, but they are executed synchronously by default. Even if the addExecutionBlock method is executed in the current thread and in other threads, it will still occupy the current thread. That’s where the NSOperationQueue comes in. Moreover, there are two types in terms of type: main queue and other queue. As soon as it is added to the queue, the start() method of the task is automatically called

  • The home side column

If you are careful, you will notice that each multi-threaded scheme has a main thread (of course, in iOS, multi-system schemes such as pthreads do not have one, because UI threading theory requires each operating system to customize it). This is a special thread that must be serialized. So tasks added to the main queue are queued up for processing on the main thread, one after another.

OBJECTIVE-C

NSOperationQueue *queue = [NSOperationQueue mainQueue];
Copy the code

SWIFT

let queue = NSOperationQueue.mainQueue()
Copy the code
  • Other queue

Because the main queue is special, there is a separate class method to get the main queue. Then the queue generated by initialization is the other queue, because these are the only two queues that do not need names except for the main queue.

  1. NSOperation can call the start method to perform the task, but by default it is executed synchronously
  2. If you add NSOperation to the NSOperationQueue, the operation in NSOperation is automatically executed asynchronously
// add an operation to NSOperationQueue - (void)addOperation:(NSOperation*)op; - (void)addOperationWithBlock:(void(^)(void))block;Copy the code

Note: Tasks on other queues are executed in parallel on other threads.

OBJECTIVE-C

//1. Create another queue NSOperationQueue *queue = [[NSOperationQueue alloc] init]; NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{NSLog(@)"% @", [NSThread currentThread]); }]; //3. Add multiple blocksfor (NSInteger i = 0; i < 5; i++) {
    [operation addExecutionBlock:^{
        NSLog(@"%ld time: %@", i, [NSThread currentThread]); }]; } //4. Queue addOperation:operation;Copy the code

SWIFT

//1. Create another queueletqueue = NSOperationQueue() //2. Create the NSBlockOperation objectlet operation = NSBlockOperation { () -> Void in
    NSLog("% @", NSThread.currentThread()) } //3. Add multiple blocksfor i in0.. <5 { operation.addExecutionBlock { () -> Voidin
        NSLog("%ld - %@", i, NSThread.currentThread()) } } //4. Queue adding task queue.addOperation(operation)Copy the code

A printout

The 2015-07-28 20:26:28. 463test[18622:4443534] <NSThread: 0x7fd022c3ac10>{number = 5, name = (null)}
2015-07-28 20:26:28.463 test[1862:4443536] NSThread: 0x7FD022E36D50 >{number = 2, name = (null)} 2015-07-28 20:26:28.463test[1862:4443535] NSThread: 0x7FD022F237f0 >{number = 4, name = (null)} 2015-07-28 20:26:28.463test[18620:4443533] NSThread: 0x7FD022D372B0 >{number = 3, name = (null)} 2015-07-28 20:26:28.463test[1862:4443534] NSThread: 0x7FD022C3AC10 >{number = 5, name = (null)} 2015-07-28 20:26:28.463test[1862:4443536] NSThread: 0x7FD022E36D50 >{number = 2, name = (null)}Copy the code

OK, now it’s time to ask, if you compare NSOperationQueue to the GCD queue, there’s no serial queue, so what if I want 10 tasks to be executed serially on another thread?

That’s the beauty of apple packaging. You don’t care about serial, parallel, synchronous, asynchronous. NSOperationQueue maxConcurrentOperationCount maximum concurrency, a parameter is used to set up to make how many tasks to perform at the same time. When you set it to 1, it is serial.

NSOperationQueue also has a way to add a task, – (void)addOperationWithBlock:(void (^)(void))block; Is it similar to the COMMUNIST Party of China? This makes it very convenient to add a task to the queue.

NSOperation has a very useful function, which is to add dependencies. For example, there are three tasks: A: download an image from the server, B: add A watermark to the image, and C: return the image to the server. This is where dependencies come in:

OBJECTIVE-C

NSBlockOperation *operation1 = [NSBlockOperation blockOperationWithBlock:^{NSLog(@)"Download picture - %@", [NSThread currentThread]); [NSThread sleepForTimeInterval: 1.0]; }]; NSBlockOperation *operation2 = [NSBlockOperation blockOperationWithBlock:^{NSLog(@"Watermark - %@", [NSThread currentThread]); [NSThread sleepForTimeInterval: 1.0]; }]; NSBlockOperation *operation3 = [NSBlockOperation blockOperationWithBlock:^{NSLog(@"Upload picture - %@", [NSThread currentThread]); [NSThread sleepForTimeInterval: 1.0]; }]; //4. Set dependency [operation2 addDependency:operation1]; // Task two depends on task one [operation3 addDependency:operation2]; NSOperationQueue *queue = [[NSOperationQueue alloc] init]; [queue addOperations:@[operation3, operation2, operation1]waitUntilFinished:NO];
Copy the code

SWIFT

//1. Task 1: Download the imagelet operation1 = NSBlockOperation { () -> Void in
    NSLog("Download picture - %@", NSThread currentThread ()) NSThread. SleepForTimeInterval} / / 2 (1.0). Task 2: Watermarkinglet operation2 = NSBlockOperation { () -> Void in
    NSLog("Watermark - %@", NSThread currentThread ()) NSThread. SleepForTimeInterval (1.0)} / / 3. Task 3: Upload pictureslet operation3 = NSBlockOperation { () -> Void in
    NSLog("Upload picture - %@", NSThread currentThread ()) NSThread. SleepForTimeInterval (1.0)} / / 4. Set dependency operation2.addDependency(operation1) // Task two depends on task one operation3.addDependency(operation2) // Task three depends on task two //5. Create a queue and join the tasklet queue = NSOperationQueue()
queue.addOperations([operation3, operation2, operation1], waitUntilFinished: false)
Copy the code

Print the result

The 2015-07-28 21:24:28. 622test[19392:4637517] Download image - <NSThread: 0x7fc10ad4d970>{number = 2, name = (null)}
2015-07-28 21:24:29.622 test[19392:4637515] Watermarking - <NSThread: 0x7fc10AF20EF0 >{number = 3, name = (null)} 2015-07-28 21:24:30.627test[19392:4637515] Upload image - <NSThread: 0x7fc10af20ef0>{number = 3, name = (null)}
Copy the code
  • Note: You cannot add interdependencies because they deadlock, such as A dependent on B, B dependent on A.
  • You can useremoveDependencyTo release the dependency.
  • You can depend on different queues, but the dependency is added to the task, regardless of the queue.

Other methods

  • NSOperation
BOOL executing; // Check whether the task is being executed BOOL FINISHED; Void (^completionBlock)(void); // Set the operation to be performed after completion - (void)cancel; // Cancel the task - (void)waitUntilFinished; // Block the current thread until the task completes // dependencies: Dependencies can be set between nsOperations to ensure the order of execution // 1. For example, operationB addDependency:operationA can be executed only after operationA completes. // Operation B depends on operation A note: We can create dependencies between nsoperations of different queues // Operation listener // 1. Can listen to the completion of an operation - (void(^)(void))completionBlock; - (void)setCompletionBlock:(void(^)(void))block;
Copy the code
  • NSOperationQueue
NSUInteger operationCount; // Get the number of tasks in queue - (void)waitUntilAllOperationsAreFinished; // Block the current thread until all tasks in the queue are completed // Maximum concurrency: The number of threads in the program can be controlled by setting the maximum concurrency // 1. The maximum number of concurrent relevant methods - (NSInteger) maxConcurrentOperationCount; - (void)setMaxConcurrentOperationCount:(NSInteger)cnt; CancelAllOperations - (void)cancelAllOperations; // 2. Cancel a single operation - (void)cancel; // suspend queue - (void)setSuspended:(BOOL)b; // YES stands for pause queue, NO stands for continue queue [queuesetSuspended:YES]; // pause queue [queuesetSuspended:NO]; // Continue queue // restore queue - (BOOL)isSuspended;Copy the code