Preface
The last article on multi-threading, from GCD API to GCD implementation, I learned NSOperation
and NSOperationQueue
gradually understood the usage scenarios and many principles of multi-threading, many of which involve other knowledge, locks, etc.
This blog is about learning the commonly used things in iOS NSThread
and understanding them pthread
(not commonly used).
Introduction to pthreads
pthread is a set of general multi-threaded APIs that can be used across platforms such as Unix/Linux/Windows. It is written in C language and requires programmers to manage the life cycle of threads themselves. It is difficult to use. We almost never use it in iOS development. Not using pthreads.
Wikipedia introduction: POSIX
Threads (English: POSIX
Threads
, often abbreviated as Pthreads
) is the thread standard POSIX
of , which defines a set of APIs for creating and manipulating threads.
The library that implements POSIX
the thread standard is often called a thread standard Pthreads
and is generally used Unix-like POSIX
in systems such Linux
as Solaris
.
Simply put, this is a thread used at the operating system level. It is implemented based on C language. It is rarely used in our OC code and is not easy to manage.
pthread_mutex
The reason for mentioning him is that pthread
I think the most contacted thing is the mutex lock itself, including the iOS NSLock lock, which is also based on the pthread_mutex encapsulated lock, just briefly mention it.
pthread_mutex
It is the data type used to implement mutex in the POSIX thread library. Mutex lock is a synchronization mechanism used to protect mutually exclusive access to shared resources in a multi-threaded environment and prevent race conditions caused by multiple threads modifying the same resource at the same time.
Here are pthread_mutex
some important operations:
- Create a mutex lock: Use
pthread_mutex_init
a function to initialize a mutex lock object and specify the properties of the lock. For example, you can choose to create a normal lock or a recursive lock. - Locking: Use
pthread_mutex_lock
a function to lock a mutex before accessing a shared resource. If the mutex is already locked by another thread, the current thread will be blocked until the mutex is available. - Attempt to lock: Use
pthread_mutex_trylock
the function to try to lock the mutex. The function returns immediately, with success or failure depending on the status of the mutex lock. - Unlock: Use
pthread_mutex_unlock
a function to release the mutex, making it available for use by other threads. The unlocking operation should occur after accessing the shared resource so that other threads can acquire the mutex lock. - Destroy the mutex lock: Use
pthread_mutex_destroy
the function to destroy the mutex lock object and release related resources.
Mutex locks provide a thread synchronization mechanism to ensure that only one thread can access protected shared resources at any time, thus avoiding data competition and uncertain behavior. It is one of the synchronization tools commonly used when writing multi-threaded programs.
Use of pthread
- First introduce the header file
#import <pthread.h>
- We need to create a thread and start the thread to perform tasks.
// 1. 创建线程: 定义一个pthread_t类型变量
pthread_t thread;
// 2. 开启线程: 执行任务
pthread_create(&thread, NULL, run, NULL);
// 3. 设置子线程的状态设置为 detached,该线程运行结束后会自动释放所有资源
pthread_detach(thread);
void * run(void *param) // 新线程调用方法,里边为需要执行的任务
{
NSLog(@"%@", [NSThread currentThread]);
return NULL;
}
pthread_create(&thread, NULL, run, NULL);
The meaning of each parameter in:
- The first parameter &thread is the thread object, a pointer to the thread identifier
- The second is the thread attribute, which can be assigned NULL
- The third run represents the pointer to the function (the corresponding function of run is the task that needs to be executed in the new thread)
- The fourth is the parameter of the running function, which can be assigned
NULL
pthreadAPI
pthread_create(pthread_t _Nullable *restrict _Nonnull, const pthread_attr_t *restrict _Nullable, void * _Nullable (* _Nonnull)(void * _Nullable), void *restrict _Nullable);
Create a threadpthread_exit(void * _Nullable)
; Terminate a threadpthread_cancel(pthread_t _Nonnull);
Interrupt the running of another threadpthread_join(pthread_t _Nonnull, void * _Nullable * _Nullable)
; Block the current thread until another thread endspthread_attr_init(pthread_attr_t * _Nonnull)
; Initialize thread propertiespthread_attr_setdetachstate(pthread_attr_t * _Nonnull, int);
Set the properties of the detached statepthread_attr_getdetachstate(const pthread_attr_t * _Nonnull, int * _Nonnull)
; Set the properties of the detached statepthread_attr_destroy(pthread_attr_t * _Nonnull)
; Delete thread attributespthread_kill(pthread_t _Nonnull, int);
Send a signal to the thread
Just understand
NSThread
Although contrast GCD
and NSOperation
are not commonly used, it is still the multi-threading technology provided by OC and is occasionally used.
NSThread
It is officially provided by Apple. It is pthread
more object-oriented in use, simple and easy to use, and can directly operate thread objects. However, programmers also need to manage the life cycle of threads themselves (mainly creation). We occasionally use NSThread during the development process. For example, it is often called [NSThread currentThread]
to display the current process information.
Create and start threads
NSThread provides three methods
- Create a thread and start the thread manually
// NSThread 先创建线程,再启动线程
- (void)JP_NSThread_1 {
// 1. 创建线程
NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(run) object:nil];
// 2. 启动线程
[thread start]; // 线程一启动,就会在线程thread中执行self的run方法
}
- Create threads and automatically start threads
// 创建线程后自动启动线程
- (void)JP_NSThread_2 {
// 1. 创建线程
[NSThread detachNewThreadSelector:@selector(run) toTarget:self withObject:nil];
}
- Implicitly create and start threads
// 隐式创建并启动线程
- (void)JP_NSThread_3 {
[self performSelectorInBackground:@selector(run) withObject:nil];
}
// 新线程调用方法,里边为需要执行的任务
- (void)run {
NSLog(@"%@", [NSThread currentThread]);
}
API provided by NSThread
This code involves NSThread
some methods and properties of the class, which are used to obtain and operate thread-related information. Here's an explanation of each part:
-
+ (NSThread *)mainThread;
This is a class method used to obtainNSThread
the object of the main thread (Main Thread) . The main thread is the main execution thread of the application, on which UI operations and other important tasks are performed. -
- (BOOL)isMainThread;
This is an instance method used to determine whether the current thread is the main thread . Returns if the thread calling this method is the main thread,YES
otherwiseNO
. -
+ (BOOL)isMainThread;
This is a class method used to determine whether the current thread is the main thread . Similar to instance methods, but can be called directly through the class name. -
NSThread *current = [NSThread currentThread];
This line of code getsNSThread
the object of the current thread . You can use this object to access and manipulate the properties and methods of the current thread. -
- (void)setName:(NSString *)n;
This is an instance method used to set the thread's name . You can identify and identify threads by giving them a meaningful name. -
- (NSString *)name;
This is an instance method used to get the name of the thread . Returns the thread's name string.
Thread state control method
- Start thread
The thread enters the ready state -> running state . When the thread task is executed, it automatically enters the death state.
- (void)start;
- Block/pause thread
+ (void)sleepUntilDate:(NSDate *)date;
This method causes the current thread to pause execution until the specified date. That is, the thread will sleep until the specified date and time is reached before continuing execution.
+ (void)sleepForTimeInterval:(NSTimeInterval)time;
This method causes the current thread to pause execution for a period of time, in seconds. The thread will sleep for the specified time before continuing execution.
These two methods can be used for thread control, such as when you need to pause the thread for a period of time before proceeding to the next step . ⚠️These methods will block the execution of the current thread, so they need to be used with caution to avoid adversely affecting the response performance of the application.
- Force stop thread
Thread enters death state
+ (void)exit;
Communication between threads
In development, we often perform time-consuming operations on sub-threads, and then return to the main thread to refresh the UI after the operation is completed. This involves communication between the child thread and the main thread.
- Perform operations on the main thread
// 在主线程上执行操作
- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)arg waitUntilDone:(BOOL)wait;
This method executes the specified method on the main thread. Parameter aSelector
is the selector (method name) of the method to be executed, and arg
is the object parameter passed to the method, wait
indicating whether to wait for execution to complete before returning. If wait
, YES
the current thread will wait for execution to complete before continuing. If NO
, it will return immediately.
- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)arg waitUntilDone:(BOOL)wait modes:(NSArray<NSString *> *)array;
// equivalent to the first method with kCFRunLoopCommonModes
This method is similar to the previous method, but it also accepts a modes
parameter that specifies the run mode in which the method is executed (Run Loop Modes)
. modes
The argument is an array of strings specifying the name of the run mode. The running mode of the main thread defines under which circumstances events and methods can be processed.
Calling this method puts the specified method into the task queue of the main thread to ensure execution on the main thread. At the same time, you can specify the running modes in which events can be processed and methods executed through the modes parameter.
This method is very useful in a multi-threaded environment, especially when operations related to interface interaction need to be performed on the main thread. For example, when updating UI elements or executing code blocks that need to be executed on the main thread, you can use this method to switch tasks to the main thread for execution, ensuring the correct thread execution context, and avoiding situations where the interface freezes or becomes unresponsive.
- Perform operations on specified thread
- (id)performSelector:(SEL)aSelector;
Is NSObject
a method of the class, used to execute the specified method in the current thread and return the return value of the method .
- (id)performSelector:(SEL)aSelector withObject:(id)object;
- (id)performSelector:(SEL)aSelector withObject:(id)object1 withObject:(id)object2;
- Perform operations on the current thread
- (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(id)arg waitUntilDone:(BOOL)wait modes:(NSArray *)array NS_AVAILABLE(10_5, 2_0);
- (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(id)arg waitUntilDone:(BOOL)wait NS_AVAILABLE(10_5, 2_0);
Demo (reference)
- Start a sub-thread and download images in the sub-thread.
- Return to the main thread to refresh the UI and display the image in UIImageView.
/**
* 创建一个线程下载图片
*/
- (void)downloadImageOnSubThread {
// 在创建的子线程中调用downloadImage下载图片
[NSThread detachNewThreadSelector:@selector(downloadImage) toTarget:self withObject:nil];
}
/**
* 下载图片,下载完之后回到主线程进行 UI 刷新
*/
- (void)downloadImage {
NSLog(@"current thread -- %@", [NSThread currentThread]);
// 1. 获取图片 imageUrl
NSURL *imageUrl = [NSURL URLWithString:@"https://ysc-demo-1254961422.file.myqcloud.com/YSC-phread-NSThread-demo-icon.jpg"];
// 2. 从 imageUrl 中读取数据(下载图片) -- 耗时操作
NSData *imageData = [NSData dataWithContentsOfURL:imageUrl];
// 通过二进制 data 创建 image
UIImage *image = [UIImage imageWithData:imageData];
// 3. 回到主线程进行图片赋值和界面刷新
[self performSelectorOnMainThread:@selector(refreshOnMainThread:) withObject:image waitUntilDone:YES];
}
/**
* 回到主线程进行图片赋值和界面刷新
*/
- (void)refreshOnMainThread:(UIImage *)image {
NSLog(@"current thread -- %@", [NSThread currentThread]);
// 赋值图片到imageview
self.imageView.image = image;
}
State transitions between threads
When we create a new thread NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(run) object:nil];
, its performance in memory is:
- When [thread start]; is called, the system puts the thread object into the schedulable thread pool, and the thread object enters the ready state, as shown in the figure below.
- Of course, there will be other thread objects in the schedulable thread pool, as shown in the figure below. Here we only care about the thread object on the left.
How to understand the thread transition of the current thread
- If the CPU now schedules the current thread object, the current thread object enters the running state. If the CPU schedules other thread objects, the current thread object returns to the ready state.
- If the CPU calls the sleep method to wait for the synchronization lock when running the current thread object, the current thread object enters the blocking state. When sleep expires or the synchronization lock is obtained, it returns to the ready state.
- If the thread task completes/exits abnormally when the CPU is running the current thread object, the current thread object enters the death state.
Summarize
Multithreading GCD
NSOperation
NSOperationQueue
NSThread
As an important member of iOS multithreading, GCD provides reliable and diverse methods with more APIs. NSOperation is more concise, and NSThread is not used so often.
Multi-threaded programming is the focus of iOS, so review it diligently.