【iOS】---pthread,NSThread

在iOS中多线程开发有四种方式,在之前我们浅浅了解了一下GCD,这期来看看pthread和NSThread
insert image description here


pehread

Introduction to pthreads

pthread is a set of general-purpose multi-threaded API, which can be used in Unix / Linux / Windows and other systems across platforms. It is written in C language and requires programmers to manage the life cycle of threads, which is difficult to use.
POSIX threads (English: POSIX Threads, often abbreviated as Pthreads) is the thread standard of POSIX, which defines a set of APIs for creating and manipulating threads.
The library that implements the POSIX thread standard is often called Pthreads, and is generally used in Unix-like POSIX systems, such as Linux and Solaris. However, implementations on Microsoft Windows also exist, such as the third-party library pthreads-w32 that directly uses the Windows API; while using the SFU/SUA subsystem of Windows, you can use a part of the native POSIX API provided by Microsoft. (Wikipedia)

How to use pthread

1. First include the header file #import <pthread.h>
2. Second, create a thread and start the thread to perform tasks

//
//  main.m
//  pthread
//
//  Created by 王璐 on 2023/5/6.
//

#import <Foundation/Foundation.h>
#import <pthread.h>
void *run(void *param) {
    
    
    
    NSLog(@"1-> %@", [NSThread currentThread]);
    NSLog(@"2-> %@", [NSThread currentThread]);
    NSLog(@"3-> %@", [NSThread currentThread]);
    NSLog(@"4-> %@", [NSThread currentThread]);
    NSLog(@"5-> %@", [NSThread currentThread]);
    NSLog(@"6-> %@", [NSThread currentThread]);
    NSLog(@"7-> %@", [NSThread currentThread]);
    return NULL;
}
int main(int argc, const char * argv[]) {
    
    
    @autoreleasepool {
    
    
        // insert code here...
        pthread_t myThread;
        int res = pthread_create(&myThread, NULL, run, NULL);
        if (res == 0) {
    
    
            NSLog(@"创建成功");
        }
        pthread_detach(myThread);
        NSLog(@"%@", [NSThread currentThread]);
    }
    return 0;
}


Please add a picture descriptionPlease add a picture description
The result is different every time it is run, and this problem has not been solved.
The meaning of each parameter in pthread_create(&thread, NULL, run, NULL);

The first parameter &thread is a thread object, a pointer to a thread identifier. The
second is a thread attribute, which can be assigned NULL.
The third run represents a pointer to a function (the function corresponding to run is a task that needs to be executed in a new thread)
. Four are the parameters of the running function, which can be assigned NULL

pthread other methods

  • pthread_create() creates a thread
  • pthread_exit() terminates the current thread
  • pthread_cancel() interrupts the execution of another thread
  • pthread_join() blocks the current thread until another thread finishes running
  • pthread_attr_init() initializes the attributes of the thread
  • pthread_attr_setdetachstate() Sets the attribute of the detachment state (determines whether this thread can be combined when it terminates)
  • pthread_attr_getdetachstate() Get the attributes of the detachment state
  • pthread_attr_destroy() delete thread attributes
  • pthread_kill() sends a signal to the thread

NSThread

NSThread is officially provided by Apple. It is more object-oriented than pthread, easy to use, and can directly manipulate thread objects. However, programmers also need to manage the life cycle of threads (mainly creation), and we occasionally use NSThread during development. For example, we often call [NSThread currentThread] to display the current process information

Create, start thread

  • Create the thread first, then start the thread
// 1. 创建线程
NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(run) object:nil];
// 2. 启动线程
[thread start];    // 线程一启动,就会在线程thread中执行self的run方法

// 新线程调用方法,里边为需要执行的任务
- (void)run {
    
    
     NSLog(@"%@", [NSThread currentThread]);
}

Please add a picture description

  • Automatically start a thread after it is created
// 1. 创建线程后自动启动线程
[NSThread detachNewThreadSelector:@selector(run) toTarget:self withObject:nil];

// 新线程调用方法,里边为需要执行的任务
- (void)run {
    
    
     NSLog(@"%@", [NSThread currentThread]);
}

insert image description here

  • implicitly creates and starts a thread
// 1. 隐式创建并启动线程
[self performSelectorInBackground:@selector(run) withObject:nil];

// 新线程调用方法,里边为需要执行的任务
- (void)run {
    
    
     NSLog(@"%@", [NSThread currentThread]);
}

Please add a picture description

// 获得主线程
+ (NSThread *)mainThread;    

// 判断是否为主线程(对象方法)
- (BOOL)isMainThread;

// 判断是否为主线程(类方法)
+ (BOOL)isMainThread;    

// 获得当前线程
NSThread *current = [NSThread currentThread];

// 线程的名字——setter方法
- (void)setName:(NSString *)n;    

// 线程的名字——getter方法
- (NSString *)name;    

  • Thread State Control Methods
    • start thread method
- (void)start;
// 线程进入就绪状态 -> 运行状态。当线程任务执行完毕,自动进入死亡状态
    • Blocking (suspending) thread methods
+ (void)sleepUntilDate:(NSDate *)date;
+ (void)sleepForTimeInterval:(NSTimeInterval)ti;
// 线程进入阻塞状态

    • force stop thread
+ (void)exit;
// 线程进入死亡状态
  • Communication between threads
    In development, we often perform time-consuming operations in 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. Let's first understand the official method of inter-thread communication on NSThread.
// 在主线程上执行操作
- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)arg waitUntilDone:(BOOL)wait;
- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)arg waitUntilDone:(BOOL)wait modes:(NSArray<NSString *> *)array;
  // equivalent to the first method with kCFRunLoopCommonModes

// 在指定线程上执行操作
- (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);

// 在当前线程上执行操作,调用 NSObject 的 performSelector:相关方法
- (id)performSelector:(SEL)aSelector;
- (id)performSelector:(SEL)aSelector withObject:(id)object;
- (id)performSelector:(SEL)aSelector withObject:(id)object1 withObject:(id)object2;

Here is an example of a demo that loads images:

/**
 * 创建一个线程下载图片
 */
- (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]);
    
    UIButton* button = [UIButton buttonWithType:UIButtonTypeClose];
    button.frame = CGRectMake(100, 100, 100, 100);
    [button setImage:image forState:UIControlStateNormal];
    [self.view addSubview:button];
}

insert image description here

NSThread thread safety and thread synchronization

thread safety

If your code is in a process where multiple threads are running at the same time, those threads may be running this code at the same time. If the result of each run is the same as that of a single-threaded run, and the values ​​of other variables are the same as expected, it is thread-safe.

If there are only read operations on global variables and static variables in each thread, but no write operations, generally speaking, this global variable is thread-safe; if multiple threads perform write operations (change variables) at the same time, generally need to consider Thread synchronization, otherwise it may affect thread safety.

thread synchronization

It can be understood that thread A and thread B cooperate together. When A executes to a certain extent, it depends on a certain result of thread B, so it stops and signals B to run; B executes as it says, and then gives the result to A; A continues to operate.

train ticket sales


@property (nonatomic, strong) UIImageView *imageView;
@property (nonatomic, assign) int ticketSurplusCount;
@property (nonatomic, strong) NSThread *ticketSaleWindow1;
@property (nonatomic, strong) NSThread *ticketSaleWindow2;



/**
 * 初始化火车票数量、卖票窗口(非线程安全)、并开始卖票
 */
- (void)initTicketStatusNotSave {
    
    
    // 1. 设置剩余火车票为 10
    self.ticketSurplusCount = 10;
    
    // 2. 设置北京火车票售卖窗口的线程
    self.ticketSaleWindow1 = [[NSThread alloc]initWithTarget:self selector:@selector(saleTicketNotSafe) object:nil];
    self.ticketSaleWindow1.name = @"北京火车票售票窗口";
    
    // 3. 设置上海火车票售卖窗口的线程
    self.ticketSaleWindow2 = [[NSThread alloc]initWithTarget:self selector:@selector(saleTicketNotSafe) object:nil];
    self.ticketSaleWindow2.name = @"上海火车票售票窗口";
    
    // 4. 开始售卖火车票
    [self.ticketSaleWindow1 start];
    [self.ticketSaleWindow2 start];

}

/**
 * 售卖火车票(非线程安全)
 */
- (void)saleTicketNotSafe {
    
    
    while (1) {
    
    
        //如果还有票,继续售卖
        if (self.ticketSurplusCount > 0) {
    
    
            self.ticketSurplusCount --;
            NSLog(@"%@", [NSString stringWithFormat:@"剩余票数:%d 窗口:%@", self.ticketSurplusCount, [NSThread currentThread].name]);
            [NSThread sleepForTimeInterval:0.2];
        }
        //如果已卖完,关闭售票窗口
        else {
    
    
            NSLog(@"所有火车票均已售完");
            break;
        }
    }
}

Two threads compete with each other, and the number of votes obtained is disordered, which obviously does not meet our needs, so we need to consider thread safety issues.
Please add a picture description

NSThread thread safety

Thread-safe solution: You can lock the thread, and when one thread performs the operation, other threads are not allowed to operate. There are many ways to implement thread locking in iOS. Various methods such as @synchronized, NSLock, NSRecursiveLock, NSCondition, NSConditionLock, pthread_mutex, dispatch_semaphore, OSSpinLock, atomic(property) set/ge, etc. For the sake of simplicity, the solutions and performance of various locks are not analyzed here, and only the simplest @synchronized is used to ensure thread safety, so as to solve the thread synchronization problem.

/**
 * 初始化火车票数量、卖票窗口(线程安全)、并开始卖票
 */
- (void)initTicketStatusSave {
    
    
    // 1. 设置剩余火车票为 10
    self.ticketSurplusCount = 10;
    
    // 2. 设置北京火车票售卖窗口的线程
    self.ticketSaleWindow1 = [[NSThread alloc]initWithTarget:self selector:@selector(saleTicketSafe) object:nil];
    self.ticketSaleWindow1.name = @"北京火车票售票窗口";
    
    // 3. 设置上海火车票售卖窗口的线程
    self.ticketSaleWindow2 = [[NSThread alloc]initWithTarget:self selector:@selector(saleTicketSafe) object:nil];
    self.ticketSaleWindow2.name = @"上海火车票售票窗口";
    
    // 4. 开始售卖火车票
    [self.ticketSaleWindow1 start];
    [self.ticketSaleWindow2 start];
    
}

/**
 * 售卖火车票(线程安全)
 */
- (void)saleTicketSafe {
    
    
    while (1) {
    
    
        // 互斥锁
        @synchronized (self) {
    
    
            //如果还有票,继续售卖
            if (self.ticketSurplusCount > 0) {
    
    
                self.ticketSurplusCount --;
                NSLog(@"%@", [NSString stringWithFormat:@"剩余票数:%d 窗口:%@", self.ticketSurplusCount, [NSThread currentThread].name]);
                [NSThread sleepForTimeInterval:0.2];
            }
            //如果已卖完,关闭售票窗口
            else {
    
    
                NSLog(@"所有火车票均已售完");
                break;
            }
        }
    }
}

Please add a picture description

thread state transition

When we create a new thread NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(run) object:nil];, the performance in memory:
Please add a picture description
when calling [thread start];, the system puts the thread object Put it into the schedulable thread pool, and the thread object enters the ready state, as shown in the figure below. Please add a picture description
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.Please add a picture description

Let's take a look at the state transition of the current thread:

  • If the CPU schedules the current thread object now, the current thread object enters the running state, and if the CPU schedules other thread objects, the current thread object returns to the ready state.
  • If the CPU calls the sleep method\waiting for the synchronization lock while running the current thread object, the current thread object enters the blocked state, and when the sleep arrives\obtains the synchronization lock, it returns to the ready state.
  • If the CPU is running the current thread object, the thread task execution is completed\abnormal forced exit, the current thread object enters the dead state.Please add a picture description

NSThread thread properties

name attribute: set the name of the thread

NSThread *thread = [[NSThread alloc] initWithBlock:^{
    
    
    NSLog(@"线程:%@ start",[NSThread currentThread]);
 }];
thread.name = @"测试线程";
[thread start];

qualityOfService attribute: set thread priority

typedef NS_ENUM(NSInteger, NSQualityOfService) {
    
    
    NSQualityOfServiceUserInteractive = 0x21,
    NSQualityOfServiceUserInitiated = 0x19,
    NSQualityOfServiceUtility = 0x11,
    NSQualityOfServiceBackground = 0x09,
    NSQualityOfServiceDefault = -1
} API_AVAILABLE(macos(10.10), ios(8.0), watchos(2.0), tvos(9.0));

NSQualityOfServiceUserInteractive has the highest priority, decreasing from top to bottom, and NSQualityOfServiceDefault is the default priority.

test:Please add a picture description

//qualityOfService属性:设置线程优先级
- (void)qualityOfServiceStudy {
    
    
    NSThread *thread1 = [[NSThread alloc] initWithBlock:^{
    
    
        NSLog(@"\n 线程:%@ start",[NSThread currentThread]);
    }];
    thread1.name = @"测试线程 1 ";
    [thread1 start];
    
    NSThread *thread2 = [[NSThread alloc] initWithBlock:^{
    
    
        NSLog(@"\n 线程:%@ start",[NSThread currentThread]);
    }];
    thread2.qualityOfService = NSQualityOfServiceUserInteractive;
    thread2.name = @"测试线程 2 ";
    [thread2 start];
}

Although thread1 starts before thread2, but the priority of thread1 is default, and the priority of thread2 is NSQualityOfServiceUserInteractive, when executing, thread2 executes before thread1.

other properties

    • @property (class, readonly, copy) NSArray<NSNumber *> *callStackReturnAddresses //The thread call will have a function call, and this property returns the virtual address array of the function call in the thread.
    • @property (class, readonly, copy) NSArray<NSString *> *callStackSymbols //This property returns the thread calling function in the form of symbols.
      //The two functions callStackReturnAddress and callStackSymbols can be used in conjunction with NSLog to track the function calls of threads, which is an important means of programming and debugging.
    • @property (readonly, retain) NSMutableDictionary *threadDictionary; //Each thread has its own stack space, and a key-value dictionary is maintained in the thread, which can be accessed anywhere in the thread.
      // You can use this dictionary to hold some information that remains constant throughout the execution of the thread.
      //For example, you can use it to store state information for multiple iterations in the Run loop throughout your thread.
    • @property (class, readonly, strong) NSThread *mainThread; // Get the main thread
    • @property (class, readonly, strong) NSThread *currentThread;// Get the current thread
    • @property NSUInteger stackSize; // The thread uses the stack size, the default is 512k
    • @property (readonly) BOOL isMainThread; // Whether it is the main thread
    • @property (class, readonly) BOOL isMainThread ; // reports whether current thread is main
    • @property (readonly, getter=isExecuting) BOOL executing ; // Whether the thread is executing
    • @property (readonly, getter=isFinished) BOOL finished ; // Whether the thread is finished
    • @property (readonly, getter=isCancelled) BOOL canceled; // Whether the thread is canceled

Please add a picture description
Please add a picture description
Please add a picture description
demo address

Guess you like

Origin blog.csdn.net/weixin_61196797/article/details/130527877