An overview of the

The NSThread class is a lightweight class inherited from the NSObjct class. An NSThread object represents a thread. It has to manage thread life cycle, synchronization, locking, and so on, so there is some performance overhead. An OC method can be called in a particular thread using the NSThread class. Multithreading is ideal when you need to perform a lengthy task that you don’t want to block other parts of your application, especially to avoid blocking the main thread of your app, which handles UI display interactions and event-related operations. Threads can also improve the performance of multi-core computers by splitting a large task into several smaller tasks.

The semantics of the NSThread class listening on a thread at runtime are similar to those of the NSOperation class. Such as canceling a thread or deciding whether it exists after a task has completed.

This article will explore NSThreads from these perspectives

An introduction to method attributes

Initializes (creates) an NSThread object

// Return an initialized NSThread object - (instanceType)init // Return an initialized NSThread object with multiple arguments // Selector: a method executed by a thread that accepts at most one argument // target: // argument: The only argument passed to the selector, Or nil - (instanceType)initWithTarget (ID) Target Selector (SEL) Selector Object (Nullable ID)argument); // iOS 10 - (instancetype)initWithBlock:(void (^)(void))block;Copy the code

Start a thread.

// Create a new thread and use a special Selector as the entry for the thread. + (void)detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(nullable id)argument; // iOS 10 + (void)detachNewThreadWithBlock:(void (^)(void))block; // Start the receiver - (void)start; // This method implements a target and a selector by default, which are used to initialize the receiver and call the method on the specified target. If you subclass NSThread, you need to override this method and use it to implement the thread body. In this case, there is no need to call the super method. // This method should not be called directly. You should start a thread by calling the start method. - (void)main;Copy the code

Using initWithTarget: selector:, initWithBlock:, detachNewThreadSelector: detachNewThreadWithBlock: create a thread are asynchronous thread.

Stopping a thread

// Block the current thread until a specified time. + (void)sleepUntilDate:(NSDate *)date; // let the thread sleep until a given interval + (void)sleepForTimeInterval:(NSTimeInterval)ti; // Terminate the current thread + (void)exit; // Change the recipient's cancel state to indicate that it should terminate - (void)cancel;Copy the code

Determining thread state

@property (readonly, getter=isExecuting) BOOL Executing; @property (readonly, getter=isFinished) BOOL Finished; @property (readonly, getter=isCancelled) BOOL cancelled;Copy the code

Main thread correlation

@property (class, readonly) BOOL isMainThread; @property (readonly) BOOL isMainThread; @property (class, readonly, strong) NSThread *mainThread;Copy the code

The execution environment

// whether the app isMultiThreaded + (BOOL)isMultiThreaded; // Return the thread object for the currently executing thread. @property (class, readonly, strong) NSThread *currentThread; // Return an array of addresses that the callback stack returned @property (class, readonly, copy) NSArray<NSNumber *> *callStackReturnAddresses; // Return an array of callback stack signals @property (class, readonly, copy) NSArray<NSString *> *callStackSymbols;Copy the code

Thread attribute correlation

@property (readonly, retain) NSMutableDictionary *threadDictionary; NSAssertionHandlerKey // Name of the receiver @Property (Nullable, copy) NSString *name; // The size of the receiver's object, in bytes. @property NSUInteger stackSize;Copy the code

Thread priority

@property NSQualityOfService qualityOfService; // Return the priority of the current thread + (double)threadPriority; // The priority of the recipient is deprecated. Use qualityOfService instead of @Property double threadPriority; // Set the priority of the current thread. SetThreadPriority (0.0-1.0, 1.0 highest) + (BOOL)setThreadPriority:(double)p;Copy the code

notice

/ / is not implemented, there is no practical significance, retain NSDidBecomeSingleThreadedNotification / / in front of the thread exits, received an NSThread object to exit the message when they sent this notice. NSThreadWillExitNotification / / when the first thread start sending this notice. This notification can be sent at most once. When NSThread first send with ` detachNewThreadSelector: toTarget: withObject: `, ` detachNewThreadWithBlock: `, ` start ` message, send a notification. Subsequent calls to these methods do not send notifications. NSWillBecomeMultiThreadedNotificationCopy the code

For inter-thread communication, the method in NSObject’s classification NSThreadPerformAdditions (in the nsthread.h file) has these features:

  1. It can be executed in either the main thread or a child thread, and it calls the aSelector method on the main thread;
  2. Methods are asynchronous
@interface NSObject (NSThreadPerformAdditions) // If wait is set to YES: the main thread will not execute the aSelector method until the current thread is finished; // If wait is set to NO, execute the aSelector method on the main thread without waiting for the current thread to finish. // If the current thread is the main thread, then the aSelector method executes immediately. - (void)performSelectorOnMainThread:(SEL)aSelector withObject:(nullable id)arg waitUntilDone:(BOOL)wait modes:(nullable NSArray<NSString *> *)array; // equals the first method where modes are kCFRunLoopCommonModes. Specifies Modes = kCFRunLoopCommonModes of Runloop in the thread. - (void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)arg waitUntilDone:(BOOL)wait; NSRunloop is not added to the child thread by default. The method in the selector is not called without a runloop added to the thread. - (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(id)arg waitUntilDone:(BOOL)wait modes:( NSArray<NSString *> *)array ; // equals the first method where modes are kCFRunLoopCommonModes. - (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(id)arg waitUntilDone:(BOOL)wait ; // Create the child thread implicitly, and create it in the background. And it's a synchronous thread. - (void)performSelectorInBackground:(SEL)aSelector withObject:(nullable id)arg ; @endCopy the code

Another method of sending a message directly to the recipient.

  1. Methods in protocol NSObject that can be executed on either the main thread or child thread. Because the synchronization task is executed on the current thread, the current thread is blocked. These methods are equivalent to calling methods directly.
// The current thread operates. - (id)performSelector:(SEL)aSelector; - (id)performSelector:(SEL)aSelector withObject:(id)object; - (id)performSelector:(SEL)aSelector withObject:(id)object1 withObject:(id)object2;Copy the code
  1. Delay operations & Operate in sequence

NSRunLoop. H file

/ / delay / * * * * * * * * * * * * * * * * of the perform of * * * * * * * * * * * * * * * * * * / @ interface NSObject (NSDelayedPerforming) / / asynchronous method, Does not block the current thread and can only execute in the main thread. Add the Selector to the main queue, and execute the Selector after the delay. If the main thread is doing business, then the Selector will not be executed until all the business has been executed, even if the delay is equal to zero. // When does' delay 'start? When you send a 'performSelector' message. Even when the main thread is blocking, it's going to count the time, and when it's done blocking, if it's delayed, it's going to execute the Selector, and if it's not, it's going to continue delaying. // Execute only in the main thread, - (void)performSelector:(SEL)aSelector withObject:(nullable id)anArgument afterDelay:(NSTimeInterval)delay inModes:(NSArray<NSRunLoopMode> *)modes; // equals the first method where modes are kCFRunLoopCommonModes. Specifies Modes = kCFRunLoopCommonModes of Runloop in the thread. - (void)performSelector:(SEL)aSelector withObject:(nullable id)anArgument afterDelay:(NSTimeInterval)delay; // Cancel the method before it reaches execution time. These two methods are called before the current target executes dealloc to ensure that it does not Crash. + (void)cancelPreviousPerformRequestsWithTarget:(id)aTarget selector:(SEL)aSelector object:(nullable id)anArgument; + (void)cancelPreviousPerformRequestsWithTarget:(id)aTarget; @interface NSRunLoop (NSOrderedPerform) @interface NSRunLoop (NSOrderedPerform) @interface NSRunLoop (NSOrderedPerform) The smaller the order argument, the higher the priority, and the earlier the execution. // The selector is the target method, and the argument is the target parameter. // The two methods set a timer for the target to execute the aSelector message at the beginning of the next runloop. The timer confirms the mode according to modes. When the timer fires, the timer attempts to queue the message out of the Runloop and execute it. This is successful if the runloop is running and is one of the modes specified, otherwise the timer waits until the runloop is one of the modes. - (void)performSelector:(SEL)aSelector target:(id)target argument:(nullable id)arg order:(NSUInteger)order modes:(NSArray<NSRunLoopMode> *)modes; - (void)cancelPerformSelector:(SEL)aSelector target:(id)target argument:(nullable id)arg; - (void)cancelPerformSelectorsWithTarget:(id)target; @endCopy the code

This article introduces most of the knowledge such as mind mapping:

use

  1. The creation thread initialized with initXXX needs to call the start method to start the thread. DetachXXX initializes the method and starts the thread directly. All threads created in this 2 ways are explicitly created threads.
//1. Manually open, Action-target NSThread * actionTargetThread = [[NSThread alloc] initWithTarget:self Selector :@selector(add:) object:nil]; [actionTargetThread start]; NSThread *blockThread = [[NSThread alloc] initWithBlock:^{NSLog(@"%s",__func__);}]; [blockThread start]; //3. Action -target [NSThread detachNewThreadSelector:@selector(add2:) toTarget:self withObject:@"detachNewThreadSelector"]; / / 4. Start the creation, the block way [NSThread detachNewThreadWithBlock: ^ {NSLog (@ "% s", __func__);}].Copy the code
  1. Thread communication

NSThreadPerformAdditions // Call the main thread method either on a child thread or on the main thread.

A. the main thread

    [self performSelectorOnMainThread:@selector(add:) withObject:nil waitUntilDone:YES];
    //[self performSelectorOnMainThread:@selector(add:) withObject:@"arg" waitUntilDone:YES modes:@[(NSRunLoopMode)kCFRunLoopDefaultMode]];
Copy the code

The child thread does not have Runloop enabled by default. You need to add it manually, otherwise the selector method will not be called.

B. the child thread

Created using initWithBlock:.

NSThread *subThread1 = [[NSThread alloc] initWithBlock:^{// 2 currentRunLoop] run]; }]; //1.2. Start a subthread [subThread1 start]; // [self performSelector:@selector(add:) onThread:subThread1 withObject:@"22" waitUntilDone:YES]; [self performSelector:@selector(add:) onThread:subThread1 withObject:@"arg" waitUntilDone:YES modes:@[(NSRunLoopMode)kCFRunLoopDefaultMode]];Copy the code

Using initWithTarget: selector: object: to create.

NSThread *subThread2 = [[NSThread alloc] initWithTarget:self Selector :@selector(startThread) object:nil]; // 1.2 Start a subthread [subThread2 start]; // [self performSelector:@selector(add:) onThread:subThread2 withObject:@"22" waitUntilDone:YES]; [self performSelector:@selector(add:) onThread:subThread1 withObject:@"arg" waitUntilDone:YES modes:@[(NSRunLoopMode)kCFRunLoopDefaultMode]]; Runloop - (void)startThread{[[NSRunLoop currentRunLoop] run]; }Copy the code

C. Background threads (implicitly creating a thread)

[self performSelectorInBackground:@selector(add:) withObject:@"arg"];
Copy the code

The 2.2 protocol NSObject method creates a synchronization task.

[NSThread detachNewThreadWithBlock: ^ {/ / direct call [the self performSelector: @ the selector (the add:) withObject: @ "XXX"];}];Copy the code

2.3 Delaying NSObject Category NSDelayedPerforming, adding asynchronous tasks that are executed on the main thread.

[self performSelector:@selector(add:) withObject:self afterDelay:2];
Copy the code

2.4 Perform NSRunLoop classification methods in NSOrderedPerform in sequence

[NSThread detachNewThreadWithBlock: ^ {NSRunLoop * currentRunloop = [NSRunLoop currentRunloop]; / / remember add port. Otherwise unable to call the selector method  [currentRunloop addPort:[NSPort port] forMode:(NSRunLoopMode)kCFRunLoopCommonModes]; [currentRunloop performSelector:@selector(add:) target:self argument:@"arg1" order:1 modes:@[(NSRunLoopMode)kCFRunLoopDefaultMode]]; [currentRunloop performSelector:@selector(add:) target:self argument:@"arg3" order:3 modes:@[(NSRunLoopMode)kCFRunLoopDefaultMode]]; [currentRunloop run]; }];Copy the code

Thread safety

Question:

Multiple threads may access the same resource at the same time. For example, multiple threads simultaneously access the same object, the same variable, the same file, and so on. If multiple threads rob the same resource at the same time, the thread is unsafe, causing data disorder and data security problems.

Solution:

Use thread synchronization technology: resources that may be robbed can be locked while being contested. Let it keep the thread in sync. There are many types of locks: read/write locks, spin locks, mutex locks, semaphores, conditional locks, etc. Mutex can be used in cases where NSThreads are likely to cause resource looting. A mutex is the sequential execution of multiple threaded tasks. In one of the following cases: Locks resources that require read and write operations.

for (NSInteger index = 0 ; index < 100; index ++) { @synchronized (self) { self.allCount -= 5; [NSThread currentThread].name, self.allcount); }}Copy the code

Thread life cycle.

The life cycle of a thread is new – ready – run – block – die. When a thread is started, it can’t “hog” the CPU all the time, so the CPU needs to switch between multiple threads, and the thread state changes accordingly.

  1. The new and ready states are created explicitly, using initWithTarget:selector: and initWithBlock: to create a thread that is not started, will be started only when the start message is sent, and will then be in the line state. Using detachNewThreadWithBlock: and detachNewThreadSelector: toTarget: display is created and started immediately. Create and way, the implicitly created and immediately start: performSelectorInBackground: withObject:.

  2. Running and Blocking A thread in the ready state is in the running state if it acquires CPU resources and starts executing the thread body (block or @selector) of the executable method.

The thread will be blocked if:

  • The thread calls the sleep method:sleepUntilDate: sleepForTimeInterval:Actively give up occupied processor resources.
  • A thread has called a blocking IO method and is blocked until the method returns. A thread is trying to acquire a synchronization monitor that is being held by another thread lock.
  • The thread is waiting for a notification (notify).
  • The program suspends the thread by calling its suspend method. However, this method is prone to deadlock, so programs should avoid using it. After the currently executing thread is blocked, other threads can get a chance to execute. The blocked thread will re-enter the ready state at an appropriate time, noting that it is ready rather than running. That is, once the blocked thread is unblocked, it must wait for the thread scheduler to schedule it again. For each of the above cases, the following specific situations will unblock the thread and make it ready again:
  • The thread that called the sleep method passed the specified time.
  • The blocking IO method called by the thread has returned.
  • The thread successfully obtained an attempt to obtain the synchronization monitor.
  • While one thread is waiting for a notification, another thread issues a notification.
  • The resume method is called on a suspended thread.
  1. The thread of death
  • Executable method execution completes and the thread ends normally.
  • Unexpected program crash.
  • The thread sends an exit message to terminate the thread.
// 1. Create: New status NSThread * actionTargetThread = [[NSThread alloc] initWithTarget:self Selector :@selector(add: object:nil); // 2. Start: Ready state [actionTargetThread start]; // executable method - (void)add (id)info{// 3. NSLog(@"%s,info %@",__func__,info); [NSThread sleepForTimeInterval:1.0]; NSLog(@"after"); } // 6. Cancel the tag [actionTargetThread Cancel]; // 7. Exit [NSThread exit];Copy the code

Note:

  • NSThread is difficult to manage multiple threads, so it is not recommended for multithreaded tasks.
  • Apple officially recommends using GCD and NSOperation.
  • [NSTread currentThread] Specifies the thread where the trace task resides. This parameter is applicable to NSTread,NSOperation, and GCD
  • The autoreleasepool is not automatically added to threads created with NSThread

reference

  • Nsthread apple document
  • The life cycle of the thread
  • Thread life cycle and five basic states