This article will first learn iOS multi-threading technology Pthreads technology. ⛽ ️ ⛽ ️

Portable Operating System Interface (English: Portable Operating System Interface (POSIX) is a set of related standards defined by IEEE for running software on various UNIX Operating systems. Its official name is IEEE Std 1003, while the international standard name is ISO/IEC 9945. This standard stems from a project that began around 1985. POSIX is an easy-to-remember name proposed by Richard Stallman (RMS) at the request of IEEE. It is basically an abbreviation for Portable Operating System Interface, with the X indicating its lineage to the Unix API. — From Baidu Encyclopedia.

Pthreads

Pthreads generally refers to POSIX threads. POSIX Threads (POSIX Threads, often abbreviated to Pthreads) is the THREADING standard for POSIX, which defines a set of apis for creating and manipulating Threads.

Pthreads profile

The library that implements the POSIX threading standard is often called Pthreads and is commonly used on UNIX-like POSIX systems such as Linux, Solaris, and macOS. However, implementations on Microsoft Windows also exist, such as pThreads-W32, a third-party library implemented directly using the Windows API, while leveraging the Windows SFU/SUA subsystem, You can use some of the native POSIX apis provided by Microsoft. Pthreads is a universal multithreading API that can be used across Unix/Linux/Windows systems. It is written in C language and requires programmers to manage the life cycle of threads themselves. Wait until after learning GCD source code will be used.

Pthreads defines a set of C types, functions, and constants that are implemented in the pthread.h header and a thread library. There are roughly 100 function calls in the Pthreads API, all of which start with “pthread_” and can be divided into four categories: (We’ll focus on the first category here: thread management, and refer to the previous locks article for the other three categories.)

  1. Thread management, such as creating threads, waiting for threads, querying thread status, etc.
  2. Mutex: Create, destroy, lock, unlock, set properties and other operations.
  3. Condition variables: create, destroy, wait, notify, set and query properties and other operations.
  4. Synchronization management between threads that use mutex

Pthreads USES

Using the Pthreads API in iOS starts with the introduction of a header file: #import . Let’s start with a simple example of starting a thread:

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.

    // Define a variable of type pthread_t, which is a pointer to a thread,
    // Its actual type is the struct _opaque_pthread_t pointer.
    pthread_t thread = NULL;
    
    // It is used to receive the return value of the child thread task in the main thread. (Can be ignored if no value is returned)
    void* thread_ret = NULL;
    
    // Prepare a variable to pass the parameter
    NSObject *objc = [[NSObject alloc] init];
    NSLog(@"objc: %p", objc);
    
    // (__bridge void *) : To pass data between C and OC, use __bridge. The purpose of bridging is to tell the compiler how to manage memory.
    CFBridgingRetain: pthread_create(&thread, NULL, run, (void *)CFBridgingRetain(objc))
    CFBridgingRelease((__bridge void *)objc); otherwise, objC will leak memory.
    
    // Passing objc directly prompts the following error, which gives us two solutions:
    // Implicit conversion of Objective-C pointer type 'NSObject *' to C pointer type 'void *' requires a bridged cast
    // Use __bridge to convert directly (no change in ownership)
    // Use CFBridgingRetain call to make an ARC object available as a +1 'void *'
    
    // MRC does not need to use bridge, can directly use objc.
    int result = pthread_create(&thread, NULL, run, (__bridge void *)(objc));
    
    if (result == 0) {
        NSLog(@"Thread created successfully 🎉🎉");
    } else {
        NSLog(@"Thread creation failed ❌, failure number: %d", result);
    }
    
    // Thread separation
    Set the state of the child thread to detached. After the thread finishes running, the system will process and release all the resources of the thread.
    // Or add pthread_detach(pthread_self()) to the child thread, where pthread_self() gets the current thread itself
    pthread_detach(thread);
    
    // Thread merge
    // Another way to do this is to do thread merge pthread_join. See the following run function, which is void * with a generic return value,
    // How do we receive its return value?
    
    // pthread_join(thread, (void**)&thread_ret);
    // NSLog(@"thread_ret: %p", thread_ret);
    
    // We must choose between thread separation and thread merge, otherwise thread resource leakage will occur. Specific details are analyzed below.
    
    NSLog(@"🧑 ‍ 💻 % @", [NSThread currentThread]);
}

void* run(void *param) {
    // sleep(2);

    NSLog(@"🏃 ‍ ♀ ️ % @ param: % p", [NSThread currentThread], param);
    
    // return param;
    return NULL;
}

// Console print:
objc: 0x6000039da810Thread created successfully 🎉🎉 🧑💻 <NSThread:0x600002ec6980>{number = 1, name = main} / / main thread

If you replace objc with your own class and rewrite the dealloc function, there will be an extra line in this line: 🍀🍀🍀 TEMP dealloc print
// When the run function is executed, objc is already free, and then print objc inside it. ????? ??

// Open the pthread_detach content by annotating the pthread_join content. If the pthread_detach content is executed for several times, the printing order of 🏃‍♀️ and 🧑 ♀️ is variable, that is, the child threads are executed asynchronously and do not block the main thread.

// annotate pthread_detach to open the pthread_join content. If pthread_join(thread, (void**)&thread_ret) is used to retrieve the return value of run, 🧑‍💻 will be executed only after 🏃‍ 💻 is completed.
Pthread_join blocks execution of the main thread until run completes and returns.🏃 ♀ ️ < NSThread:0x600002ea36c0>{number = 6, name = (null)} param: 0x6000039da810 // The run function starts the child thread, and obj is passed in
Copy the code

Pthread_detach Different print orders when threads are separated.

2020- 11- 13 16:30:44.152497+0800 Simple_iOS[50694:9047896] objc: 0x600001558a40
2020- 11- 13 16:30:44.152932+0800 Simple_iOS[50694:9048038] 🏃 ♀ ️ < NSThread:0x6000002d4fc0>{number = 6, name = (null)} param: 0x600001558a40
2020- 11- 13 16:30:44.153245+0800 Simple_iOS[50694:9047896] Thread creation succeeded 🎉🎉2020- 11- 13 16:30:44.155304+0800 Simple_iOS[50694:9047896] 🧑 💻 < NSThread:0x60000025acc0>{number = 1, name = main}
Copy the code
2020- 11- 13 16:31:23.100374+0800 Simple_iOS[50700:9048791] objc: 0x600003ab4490
2020- 11- 13 16:31:23.100671+0800 Simple_iOS[50700:9048791] Thread creation succeeded 🎉🎉2020- 11- 13 16:31:23.100921+0800 Simple_iOS[50700:9048791] 🧑 💻 < NSThread:0x600002da3040>{number = 1, name = main}
2020- 11- 13 16:31:23.100770+0800 Simple_iOS[50700:9048878] 🏃 ♀ ️ < NSThread:0x600002df9580>{number = 6, name = (null)} param: 0x600003ab4490
Copy the code

Here is a digress, when looking at the source code, we often encounter the type of named suffix _T /ref, such as Spinlock_t, Weak_table_t, weak_entry_t, etc. The suffix T is used to represent struct, this is because there is no class definition in C, It is a procedural-oriented language. In C, types are represented as structs, so an _t is added just to identify the type. One of the actual schemes in iOS multithreading is pThread

In mixed development, if you pass data between C and OC, you need to use __bridge. The purpose of bridge is to tell the compiler how to manage memory, MRC does not need to use bridge; In the OC, if is the ARC development, the compiler will at compile time, according to the code structure, automatically add retain/release/autorelease. However, ARC is only responsible for managing memory management for the OC part, not for C code. Therefore, in the development process, if the use of C language framework contains functions such as retain/create/copy/new, most of them need to release, otherwise there will be memory leakage. The CFBridgingRetain and CFBridgingRelease above are used together. One of the actual schemes in iOS multithreading is pThread

Pthread_t definition

Pthread_t is a pointer to a thread, which on iOS is: __darwin_pthread_t. Here’s a look at the source definition:

typedef __darwin_pthread_t pthread_t;

typedef struct _opaque_pthread_t* __darwin_pthread_t;

struct _opaque_pthread_t {
    long __sig;
    struct __darwin_pthread_handler_rec* __cleanup_stack;
    char __opaque[__PTHREAD_SIZE__];
};

struct __darwin_pthread_handler_rec {
    void (*__routine)(void *);    // Routine to call The entry function of the thread, that is, the task that needs to be performed in the new thread
    void *__arg;            // Argument to pass __routine Argument
    struct __darwin_pthread_handler_rec* __next; // Can be understood as a pointer to the next node in the list
};
Copy the code

The pthread_t is actually a pointer to the _opaque_pthread_t structure.

The pthread_create thread is created

Pthread_create is a function for creating threads on UNIX-like operating systems (Unix, Linux, Mac OS X, and so on). Its function is to create a thread (in effect, to determine the entry point to call that thread function), and once the thread is created, the related thread function is run. Pthread_create returns 0 on success; If an error occurs, the error number is returned and the content in pthread_t * __RESTRICT is undefined. Let’s look at its function declaration:

__API_AVAILABLE(macos(10.4), ios(2.0))
#if! _PTHREAD_SWIFT_IMPORTER_NULLABILITY_COMPAT
int pthread_create(pthread_t _Nullable * _Nonnull __restrict,
        const pthread_attr_t * _Nullable __restrict,
        void * _Nullable (* _Nonnull)(void * _Nullable),
        void * _Nullable __restrict);
#else
/ / compatible with Swift
int pthread_create(pthread_t * __restrict,
        const pthread_attr_t * _Nullable __restrict,
        void *(* _Nonnull)(void *), void * _Nullable __restrict);
#endif // _PTHREAD_SWIFT_IMPORTER_NULLABILITY_COMPAT
Copy the code

When a thread is successfully created, the memory unit pointed to by pthread_t * __RESTRICT is set to the contents of the newly created thread, and const pthread_attr_t * __RESTRICT is used to specify thread attributes. The newly created thread runs from the void * (* _Nonnull)(void *) function address. This function pointer points to the starting address of the thread-enabled callback function, and the last void * _Nullable __RESTRICT is its argument. If you need more than one parameter, you can pass objects that contain different attribute values to pass. This design allows a thread to have some proprietary data ready before it is created, typically using the this pointer in C++ programming.

The four parameters can be summarized as follows:

  1. The first argument is a pointer to the thread. When a new thread is successfully created, the thread handle is returned to the caller to manage the thread.
  2. The second parameter is used to set the thread property, which can be passedNULL.
  3. The third argument is the starting address from which the thread runs the function. Tasks that need to be executed in a new thread. This function has a return valuevoid *, the return value can be passedpthread_join()Interface acquisition)
  4. The fourth argument is a passable argument to the run functionNULL.

Pthread_join thread merge

Now that we have the thread creation details outlined above, let’s go back to our example code and first open the comment on pthread_JOIN and comment on pthread_detach. Run the program to get the following print:

2020- 11- 13 16:33:09.516744+0800 Simple_iOS[50716:9050696] objc: 0x600000202bd0
2020- 11- 13 16:33:09.517021+0800 Simple_iOS[50716:9050696] Thread creation succeeded 🎉🎉2020- 11- 13 16:33:11.518575+0800 Simple_iOS[50716:9050922] 🏃 ♀ ️ < NSThread:0x600001580000>{number = 7, name = (null)} param: 0x600000202bd0
2020- 11- 13 16:33:11.519041+0800 Simple_iOS[50716:9050696] thread_ret: 0x600000202bd0
2020- 11- 13 16:33:11.519384+0800 Simple_iOS[50716:9050696] 🧑 💻 < NSThread:0x600001500fc0>{number = 1, name = main}
Copy the code

🎉🎉 and 🏃♀️ execute time difference of 2 seconds, this 2 seconds is run inside the sleep(2) execute time, and then print the last of the run. Thread blocks our main thread until the run function/task inside the thread completes and returns.

Here, for comparison, try running the contents of pthread_detach to see the difference between the print and pthread_join. Calling pthread_join(thread, (void**)&thread_ret) merges our manually created thread thread with the main thread. The first argument to pthread_JOIN is the newly created thread handle, and the second argument receives the return value of the thread. Pthread_join blocks execution of the main process until the merged thread finishes executing. Since thread threads return param after they have finished, pthread_JOIN naturally returns the same value as objC, which we originally created. This is confirmed by the same object address in the output (see some articles describing thread merging as thread waiting, For example, the main thread is waiting for a thread, which is logically identical to the merge thread. It is important to note that a thread cannot be waited by multiple threads (merge), otherwise the first thread that received the signal will return successfully, and other threads that called pthread_JOIN will return error code ESRCH. Look at the declaration of the pthread_join function:

__API_AVAILABLE(macos(10.4), ios(2.0))
int pthread_join(pthread_t , void * _Nullable * _Nullable)
        __DARWIN_ALIAS_C(pthread_join);
Copy the code

What does the pthread_join function do? What is thread merging?

One of the first things we need to clarify is what thread merging is. As we’ve seen from the previous statement, pthread_create is responsible for creating a thread. Threads are a system resource, just like memory, and threads themselves have to occupy a certain amount of memory. A well-known problem in C or C++ programming is that if a block of memory is allocated through malloc() or new, it must be recycled using free() or delete, otherwise it will cause a well-known memory leak. Since threads are no different from memory, there must be reclamation to create, or else there will be another well-known resource leak problem, which is also a serious problem. Thread merging is reclaiming thread resources.

Thread merging is an active reclamation of thread resources. A thread merge occurs when one process or thread calls the pthread_join function for another thread. This interface blocks the calling process or thread until the merged thread terminates. When the merged thread terminates, the pthread_JOIN interface reclaims the thread’s resources and returns the thread’s return value to the merger. Another thread reclamation mechanism corresponding to thread merge is thread separation, which is called by the pthread_detach interface. Thread separation is analyzed below.

Pthread_detach thread separation

First look at the pthread_detach function declaration:

__API_AVAILABLE(macos(10.4), ios(2.0))
int pthread_detach(pthread_t);
Copy the code

Thread separation is to reclaim thread resources by the system automatically, that is, when the separated thread ends, the system will automatically reclaim its resources. Since thread detach is the automatic recycling mechanism that starts the system, the program cannot obtain the return value of the detached thread. This allows the pthread_detach interface to have only one parameter, which is the handle to the detached thread.

Thread merging and thread splitting are used to reclaim thread resources and can be used according to different business scenarios. Whatever the reason, you have to choose one or the other, or you’ll have a resource leak problem that’s just as scary as a memory leak.

Pthread_attr_t Thread attribute

When we called the pthread_create function to create a thread, the second argument was to set the property of the thread and we passed NULL. We can pass in a pointer to pthread_attr_t when we need to set the thread properties (pthread_attr_t is actually an alias of the _OPAque_pthread_attr_t structure).

To set different attributes for threads, POSIX defines a series of attribute setting functions. We can use the pthread_attr_init interface to initialize thread property structures and the pthread_attr_destroy interface to destroy thread property structures. And pthread_attr_get XXX/pthread_attr_set XXX functions associated with individual attributes.

First let’s take a look at the pthread_attr_t definition.

typedef __darwin_pthread_attr_t pthread_attr_t;

typedef struct _opaque_pthread_attr_t __darwin_pthread_attr_t;

struct _opaque_pthread_attr_t {
    long __sig;
    char __opaque[__PTHREAD_ATTR_SIZE__];
};
Copy the code

Pthread_attr_init function declaration:

__API_AVAILABLE(macos(10.4), ios(2.0))
int pthread_attr_init(pthread_attr_t *);
Copy the code

The pthread_attr_destroy function declares:

__API_AVAILABLE(macos(10.4), ios(2.0))
int pthread_attr_destroy(pthread_attr_t *);
Copy the code

Before setting the thread attribute pthread_attr_t, we usually call pthread_attr_init to initialize it, and then call the corresponding attribute setting function. Before looking at the specific attribute setting function, let’s take a look at what attributes threads have.

So what are the properties of threads? In the function list of pThread. h file, we can see that In Apple platform (iOS/macOS), Apple implements the following thread properties according to the POSIX thread standard:

  • Separation properties
  • Binding properties (Scope properties)
  • Full stack Alert zone property
  • Stack size property
  • Address of the stack
  • Scheduling attributes (including algorithm, scheduling priority, inheritance)

Each of these properties is described in detail below.

Separation properties

The first thing you can see in the pthread.h file is that there are two set and read interfaces associated with separating properties: pthread_attr_setdetachState and pthread_attr_getdetachState.

__API_AVAILABLE(macos(10.4), ios(2.0))
int pthread_attr_setdetachstate(pthread_attr_t *, int);

__API_AVAILABLE(macos(10.4), ios(2.0))
int pthread_attr_getdetachstate(const pthread_attr_t *, int *);
Copy the code

As mentioned earlier, threads can be merged and detached. The detached property lets the thread decide before creation that it should be detached. If this property is set, there is no need to call pthread_JOIN or pthread_detach to reclaim thread resources. The second argument to the pthread_attr_setdetachState function has two values, PTHREAD_CREATE_DETACHED (detached) and PTHREAD_CREATE_JOINABLE (merged, also the default). These are two macro definitions. Defined at the top of the pthread.h file:

#define PTHREAD_CREATE_JOINABLE      1
#define PTHREAD_CREATE_DETACHED      2
Copy the code

The following is an example of separating properties:

.pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);

int result = pthread_create(&thread, &attr, run, (__bridge void*)(objc)); .Copy the code

Binding properties (Scope properties)

The apis and macros associated with binding properties (scope properties) in pThread. h are defined as follows:

__API_AVAILABLE(macos(10.4), ios(2.0))
int pthread_attr_setscope(pthread_attr_t *, int); / / set

__API_AVAILABLE(macos(10.4), ios(2.0))
int pthread_attr_getscope(const pthread_attr_t * __restrict, int * __restrict); / / read

/* We only support PTHREAD_SCOPE_SYSTEM */ PTHREAD_SCOPE_PROCESS is not supported on either iOS or macOS
#define PTHREAD_SCOPE_SYSTEM         1 / / binding
#define PTHREAD_SCOPE_PROCESS        2 / / not binding
Copy the code

Due to the current understanding of this attribute is limited, here will be the first Linux big guy excerpts of the original post.

When it comes to this binding property, another concept comes up: Light Weight processes (LWP). Light processes have the same concept as kernel threads in Linux systems and belong to the scheduling entity of the kernel. A light process can control one or more threads. By default, for a program with n threads, the state of how many light processes are started and which light processes control which threads are controlled by the operating system is called unbound. The meaning of binding is easy to understand, as long as you specify that a thread is “tied” to a light process, you can call it bound. The bound thread has higher response times because the operating system’s scheduling body is light processes, and the bound thread ensures that it always has a light process available when needed. That’s what binding properties are for.

The interface for setting binding properties is pthread_attr_setscope(), which is fully defined as: int pthread_attr_setSCOPE (pthread_attr_t *attr, int scope); It takes two parameters, the first is a pointer to the thread property object, and the second is the binding type, which has two values: PTHREAD_SCOPE_SYSTEM (bound) and PTHREAD_SCOPE_PROCESS (unbound).

I wonder if you find a contradiction in this article here. It is this binding property that contradicts what we said earlier about NPTL. I mentioned in the introduction of NPTL that there is an M: N threading scheme in the industry related to this binding property. But I also said that NPTL doesn’t do this because Linux is “stupid” and uses a “1:1” solution instead. In other words, Linux threads are always bound. Yes, Linux threads are always bound, so PTHREAD_SCOPE_PROCESS doesn’t work in Linux and returns an ENOTSUP error.

Since Linux does not support thread unbinding, why provide this interface? The answer is compatibility! This interface is provided because Linux’s NTPL is claimed to be POSIX compliant, and binding properties are required by the POSIX standard. If you’re just writing multithreaded programs in Linux, you can ignore this property entirely. If you ever come across a system that supports this feature, remember I told you about it 🙂 using threads in Linux

Sets the thread’s __scope attribute. The scope property represents the scope of competing cpus between threads, that is, the effective range of thread priorities. POSIX standards define two values: PTHREAD_SCOPE_SYSTEM, which means that all threads in the system compete for CPU time, and PTHREAD_SCOPE_PROCESS, which means that only threads in the same process compete for CPU time. The default is PTHREAD_SCOPE_PROCESS. Currently Linux Threads only implements the PTHREAD_SCOPE_SYSTEM value. Introduction to the thread attribute pthread_attr_t

Stack size property

The API associated with stack size attributes in the pthread.h file is as follows:

__API_AVAILABLE(macos(10.4), ios(2.0))
int pthread_attr_setstacksize(pthread_attr_t *, size_t);

__API_AVAILABLE(macos(10.4), ios(2.0))
int pthread_attr_getstacksize(const pthread_attr_t * __restrict, size_t * __restrict);
Copy the code

As you can see from the previous examples, the main function of the child thread has a very similar property to the viewDidLoad function of the main thread in that it can have local variables. Although threads of the same process share memory space, its local variables do not. The reason is that local variables are stored on the stack, and different threads have different stacks. Linux allocates 8 MB stack space per thread by default. If you feel that this space is not enough, you can increase it by modifying the stack size property of the thread. The interface for modifying the thread stacksize attribute is pthread_attr_setstackSize, whose second argument is the stacksize, in bytes. Note that the thread stack must not be smaller than 16 KB, and should be allocated as integer multiples of 4 KB (32-bit systems) or 2 MB (64-bit systems), that is, the size of the page in memory. Also, changing the thread stack size is risky, and if you don’t know what you’re doing, it’s best to leave it alone.

Full stack Alert zone property

The API associated with the full stack alert attribute in pthread.h is as follows:

__API_AVAILABLE(macos(10.4), ios(2.0))
int pthread_attr_setguardsize(pthread_attr_t *, size_t);

__API_AVAILABLE(macos(10.4), ios(2.0))
int pthread_attr_getguardsize(const pthread_attr_t * __restrict, size_t * __restrict);
Copy the code

Since threads have a stack, and there is a size limit, you are bound to run out of stack. A full stack of threads is a very dangerous thing, because it can lead to destruction of kernel space, and if exploited by the right people, the consequences can be disastrous. To prevent this from happening, Linux sets a full stack alert for thread stacks. This area is typically a page, an extension of the thread stack. Once code accesses this area, it sends a SIGSEGV signal to notify.

While the full stack alert can play a role in security, it also has the disadvantage of wasting memory space and making the system slow for memory tight systems. So there’s a need to shut down the perimeter. At the same time, if we change the size of the thread stack, the system will assume that we manage the stack ourselves and will disable the alert zone, turning it on if necessary. Pthread_attr_setguardsize is the interface that modifies the full stack alert zone property. Its second parameter is the alert zone size, in bytes. Like setting the thread stack size property, it should be allocated as integer multiples of 4KB or 2MB as possible. When the alert zone size is set to 0, the alert zone is turned off. It wastes a bit of memory, but it greatly improves security, so the loss is worth it. And whenever you change the size of the thread stack, be sure to set this warning zone at the same time.

Scheduling attributes (including algorithm, scheduling priority, inheritance)

The apis and macros related to scheduling properties (including algorithms, scheduling priorities, inheritance) in pthread.h/ pthread_imp.h are defined as follows:

__API_AVAILABLE(macos(10.4), ios(2.0))
int pthread_attr_setschedpolicy(pthread_attr_t *, int); // Set the scheduling algorithm (policy)

__API_AVAILABLE(macos(10.4), ios(2.0))
int pthread_attr_getschedpolicy(const pthread_attr_t * __restrict, int * __restrict); // Read scheduling algorithm (policy)

// Three strategies
#define SCHED_OTHER                1 / / other
#define SCHED_FIFO                 4 // First in, first out
#define SCHED_RR                   2 / / polling
Copy the code
#ifndef __POSIX_LIB__
struct sched_param { int sched_priority;  char __opaque[__SCHED_PARAM_SIZE__]; };
#endif

__API_AVAILABLE(macos(10.4), ios(2.0))
int pthread_attr_setschedparam(pthread_attr_t * __restrict,
        const struct sched_param * __restrict); // Set the thread priority

__API_AVAILABLE(macos(10.4), ios(2.0))
int pthread_attr_getschedparam(const pthread_attr_t * __restrict,
        struct sched_param * __restrict); // Read the thread priority
Copy the code
__API_AVAILABLE(macos(10.4), ios(2.0))
int pthread_attr_setinheritsched(pthread_attr_t *, int); // set the inheritance

__API_AVAILABLE(macos(10.4), ios(2.0))
int pthread_attr_getinheritsched(const pthread_attr_t * __restrict, int * __restrict); // Read the inheritance

#define PTHREAD_INHERIT_SCHED        1 // Have the right to inherit
#define PTHREAD_EXPLICIT_SCHED       2 // Give up the inheritance
Copy the code

The scheduling properties of a thread are algorithm, priority and inheritance.

Linux provides three thread scheduling algorithms: polling, fifO, and others. The polling and first-in, first-out scheduling algorithms are specified by POSIX standards, while the others represent the scheduling algorithms that Linux considers more appropriate, so the default scheduling algorithm is the other. Polling and FIFO are real-time scheduling algorithms. Polling refers to time slice rotation. When the time slice of the thread runs out, the system will reallocate the time slice and place it at the end of the ready queue, so as to ensure that polling tasks with the same priority get a fair CPU usage time. First in, first out (FIFO) is a first-come, first-served process that runs until a higher priority thread appears or abandons itself.

The interface for setting the thread scheduling algorithm is pthread_attr_setschedpolicy, whose second parameter has three values: SCHED_RR (polling), SCHED_FIFO (first-in, first-out) and SCHED_OTHER (other). Linux thread priorities are not the same as process priorities, as explained in the previous article. Linux thread priorities range from 1 to 99, with a higher number indicating a higher priority. And note that priorities are only valid if SHCED_RR or SCHED_FIFO scheduling algorithms are used. For threads with SCHED_OTHER scheduling algorithms, the priority is always 0.

The interface for setting a thread’s priority is pthread_attr_setschedparam, and the sched_PRIORITY field in the sched_param structure is the thread’s priority.

Moreover, even with SCHED_RR or SCHED_FIFO scheduling algorithms, thread priorities are not arbitrarily set. First, the process must be running as root; Second, you need to give up the inheritance of the thread. What is the right of inheritance? When a new thread is created, the new thread inherits the scheduling property of the parent thread (the creator thread). If you do not want the new thread to inherit the scheduling properties of the parent thread, you must abandon the inheritance.

The interface for setting thread inheritance is pthread_attr_setinheritsched. The second parameter has two values: PTHREAD_INHERIT_SCHED (inheriting) and PTHREAD_EXPLICIT_SCHED (forgoing inheriting), new threads are inheriting by default.

Well, the thread attribute is introduced here first, because there is no iOS/macOS material, here borrowed from Linux UNDER THE POSIX thread standard, although the platform is different, but the basic understanding and processing are the same, so it does not prevent us to understand and learn the thread attribute. Let’s move on to the other interfaces in the pThread. h file.

Pthread_kill sends a signal to the specified thread

Pthread_kill is used to send signals to the specified thread. Signal (SIGKILL, SIG_handler) is used to process signals in the created thread. If SIGQUIT is sent to a thread but the thread does not implement signal handler, the entire process exits. The function declaration is as follows:

__API_AVAILABLE(macos(10.4), ios(2.0))
int pthread_kill(pthread_t.int);
Copy the code

The first parameter is the thread to which the signal is sent, and the second parameter is the signal to which the signal is passed. This parameter is usually greater than 0. Common semaphore macros can be seen in #import <signal.h>. Signal 0 is a reserved signal that is used to test for thread existence.

Pthread_kill returns the following value:

  • 0: The call is successful
  • ESRCH: The thread does not exist
  • EINVAL: Signal is invalid
  • A method to test whether a thread exists/terminates

If the signal is not processed within the thread, the default handler is called, such as SIGQUIT to exit and terminate the thread, SIGKILL to kill the thread, and so on. Signal (SIGQUIT, SIG_process_routine) can be called to custom signal handlers.

Pthread_cancel Interrupts the execution of a specified thread

Pthread_cancel Sends a termination signal to the specified thread, returning 0 on success or a non-zero value otherwise. Success does not mean that the thread will terminate. The function declaration is as follows:

__API_AVAILABLE(macos(10.4), ios(2.0))
int pthread_cancel(pthread_t) __DARWIN_ALIAS(pthread_cancel);
Copy the code

If you want to terminate each thread when the entire program exits, you should use the pthread_join function after successfully sending the CANCEL command and wait for the specified thread to exit completely before continuing execution. Otherwise, it is easy to cause “segment error”.

Pthread_setcancelstate Sets the response of this thread to Cancel

#define PTHREAD_CANCEL_ENABLE        0x01  /* Cancel takes place at next Cancellation point */ 
#define PTHREAD_CANCEL_DISABLE       0x00  /* postpone postpone // postpone postpone */

__API_AVAILABLE(macos(10.4), ios(2.0))
int pthread_setcancelstate(int state, int * _Nullable oldstate)
        __DARWIN_ALIAS(pthread_setcancelstate);
Copy the code

Set how your thread reacts to Cancel signals. State has two values: PTHREAD_CANCEL_ENABLE and PTHREAD_CANCEL_DISABLE, which mean “CANCLED” and “Cancel”, respectively. Old_state if not NULL is saved to the original Cancel state for recovery.

PTHREAD_CANCEL_ENABLE: indicates that the processing cancellation signal can be received, the thread status is set to CANCEl, and the task execution is terminated.

PTHREAD_CANCEL_DISABLE: The cancel signal is ignored and the task continues.

Pthread_setcanceltype Sets the cancellation time for this thread

#define PTHREAD_CANCEL_DEFERRED      0x02  /* Cancel waits until cancellation point */
#define PTHREAD_CANCEL_ASYNCHRONOUS  0x00  /* Cancel occurs immediately // Cancel occurs immediately */

__API_AVAILABLE(macos(10.4), ios(2.0))
int pthread_setcanceltype(int type, int * _Nullable oldtype)
        __DARWIN_ALIAS(pthread_setcanceltype);
Copy the code

Set the execution time of the cancel action for this thread. Type can be either of the following values: PTHREAD_CANCEL_DEFERRED and PTHREAD_CANCEL_ASYNCHRONOUS, valid only when Cancel is in Enable state, continue to the next Cancel point after receiving a signal and then exit. (Recommended, Since memory collection must be handled before the thread terminates to prevent memory leaks, manually setting the cancellation point gives us the freedom to handle the timing of memory collection and cancel immediately (exit) (this operation is not recommended because it may cause memory leaks and other problems). Oldtype is saved to the original cancel action type if it is not NULL.

This function should be executed at the start of the thread. If there are any resource requests within the thread, the PTHREAD_CANCEL_DEFERRED setting should be selected and the thread should exit at the exit point (pthread_testCancel is used to define the exit point).

pthread_testcancel

__API_AVAILABLE(macos(10.4), ios(2.0))
void pthread_testcancel(void) __DARWIN_ALIAS(pthread_testcancel);
Copy the code

Check whether the thread is in Canceld state, if so, cancel, otherwise return directly. This function is executed in the thread, and the execution position is the thread exit position. Before executing this function, the related resource request in the thread must be released, otherwise it is easy to cause memory leak.

A thread cancels by sending a Cancel signal to the target thread, but it is up to the target thread to decide how to handle the Cancel signal, whether to ignore it, terminate it immediately, or continue running to the cancelation-point, depending on the Cancelation state.

The default handling for a thread that receives CANCEL (the default state of the pthread_CREATE thread) is to continue running to the cancellation point, setting a CANCELED state. The thread continues running only after Cancelation-point.

Pthread_self gets the current thread itself

__API_AVAILABLE(macos(10.4), ios(2.0))
pthread_t pthread_self(void);
Copy the code

Gets the current thread itself inside the thread. Such as [NSThread currentThread].

Pthread_equal compares whether two threads are equal

__API_AVAILABLE(macos(10.4), ios(2.0))
int pthread_equal(pthread_t _Nullable, pthread_t _Nullable);
Copy the code

Compare the two threads.

Pthread_exit terminates the current process

__API_AVAILABLE(macos(10.4), ios(2.0))
void pthread_exit(void * _Nullable) __dead2;
Copy the code

The thread terminates execution by calling the pthread_exit function, just as the process calls exit when it terminates. This function terminates the calling thread and returns a pointer to an object.

Thread local storage

Thread Local Storage (THREAD-local Storage) Thread Local Storage (THread-local Storage)

Threads within the same process can share memory address space, data exchange between threads can be very fast, which is the most significant advantage of threads. However, multiple threads accessing shared data requires expensive synchronization overhead (locking), can cause synchronization related bugs, and even more troublesome is that some data does not want to be shared at all, which is another disadvantage.

Errno in the C library is the most typical example. Errno is a global variable that holds the error code for the last system call. There is no problem in a single-threaded environment. But in a multithreaded environment, because errno can be modified by all threads, it is difficult to determine which system call errno represents. This is known as “non-thread-safe”.

In addition, from the perspective of modern technology, many times the purpose of multithreading is not parallel processing of shared data. More due to the introduction of multi-core CPU technology, in order to make full use of CPU resources and parallel computing (not interfering with each other). In other words, most of the time each thread only cares about its own data and does not need to synchronize with others.

To solve these problems, there are many solutions. Such as using global variables with different names. But a global variable with a fixed name like errno does not. As mentioned earlier, allocating local variables in the thread stack is not shared between threads. However, it has the disadvantage that other functions inside the thread are hard to access. The simplest solution to this problem is Thread Local Storage, or TLS. With TLS, errno reflects the error code of the last system call in the thread, which is thread-safe. Linux (iOS/macOS) provides full support for TLS through the following interfaces:

typedef __darwin_pthread_key_t pthread_key_t;
typedef unsigned long __darwin_pthread_key_t;

__API_AVAILABLE(macos(10.4), ios(2.0))
int pthread_key_create(pthread_key_t *, void (* _Nullable)(void *));

__API_AVAILABLE(macos(10.4), ios(2.0))
int pthread_key_delete(pthread_key_t);

__API_AVAILABLE(macos(10.4), ios(2.0))
int pthread_setspecific(pthread_key_t , const void * _Nullable);

__API_AVAILABLE(macos(10.4), ios(2.0))
void* _Nullable pthread_getspecific(pthread_key_t);
Copy the code

The pthread_KEY_CREATE interface is used to create a thread-local store. The first parameter returns a handle to the store, which needs to be held in a global variable so that all threads can access it. The second argument is a callback function pointer to thread-local data, which can be passed NULL if you want to control the thread-local data life cycle yourself.

The pthread_KEY_DELETE interface is used to reclaim thread-local storage. Its only argument is the handle to the reclaimed storage area.

The pthread_getSpecific and pthread_setSpecific interfaces are used to get and set data from thread-local storage, respectively. The two interfaces will have different results for different threads (the same thread will have the same results), which is the key to thread-local storage.

NSThread

NSThread is a class that inherits from NSObject and is used to manage and manipulate threads. After Pthreads, nsThreads are very nice to see. It seems that any classes with the NS prefix are almost all inherited classes from NSObject, and with ARC’s help the compiler does the final release and destruction for us, so we just create and use them. In addition, their almost identical use logic also makes us easy to learn, easy to use and easy to use. Now let’s get into nsthreads.

One NSThread object corresponds to one thread. Compared to Pthreads, it operates and manages threads in a more object-oriented manner. Although we still need to manage the thread life cycle manually, it is limited to creation. We can understand the process of thread creation as the creation of NSThread objects. As for the final task completion and thread resource recycling, the system will help us deal with it, so it is not the most easy to use compared to GCD. In the use of GCD, we can completely ignore the creation and destruction of threads. The use of GCD and the source part of the next article, so let’s first look at the use of NSThread! ⛽ ️ ⛽ ️

Nsthreads create and start threads

The nsThread. h file lists the ways in which all NSthreads can be created and started, and provides the ability to perform functions (or tasks) in the thread in the form of selectors or blocks. Also at the bottom of the file is a category of NSObject NSThreadPerformAdditions that lists a set of instance methods for executing functions (or tasks) on the main thread, background thread, or designated thread. That is, we can use these multithreaded apis with any object that inherits from NSObject classes, making it much easier to use multithreading to perform tasks in development. Here’s an example of NSThread usage:

  • Use the NSThreadallocinitMethod explicitly creates an NSThread object (creates a thread), and then callsstartThe function starts the thread.
- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.

    // Create an object as an argument to the thread function. See that the run function prints the address of the object exactly the same
    NSObject *objc = [[NSObject alloc] init];
    NSLog(@"objc: %p", objc);
    
    // 1. Create NSThread object (create thread)
    NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(run:) object:objc];
    
    // Set the thread name
    [thread setName:@"thread"];
    // Set the priority from 0 to 1, with 1 being the highest
    [thread setThreadPriority:0.9];
    
    // 2. Start thread
    [thread start];
    // (does not block the current thread, does not wait for the run function to complete execution before executing the following instruction)
    
    // 1. Create the NSThread object (create thread). The thread performs tasks in the form of blocks, which is more compact
    NSThread *thread2 = [[NSThread alloc] initWithBlock:^{
        sleep(2);
        NSLog(@"🙆 ‍ came ️ % @", [NSThread currentThread]);
    }];
    // 2. Start thread
    [thread2 start];
    // (does not block the current thread, does not wait for the block to complete before executing the following instruction)
    
    NSLog(@"🧑 ‍ 💻 % @", [NSThread currentThread]);
}

- (void)run:(NSObject *)param {
    sleep(3);
    NSLog(@"🏃 ‍ ♀ ️ % @ param: % p", [NSThread currentThread], param);
}

/ / print:
2020- 11- 15 11:35:30.178059+0800 Simple_iOS[57531:9389410] objc: 0x6000024e42e0
2020- 11- 15 11:35:30.178355+0800 Simple_iOS[57531:9389410] 🧑 💻 < NSThread:0x6000033ea7c0>{number = 1, name = main} ⬅️ NSThread is the main thread2020- 11- 15 11:35:32.183285+0800 Simple_iOS[57531:9389593] 🙆 came ️ < NSThread:0x6000033ba900>{number = 8, name = (null)} ⬅️ NSThread is a child thread. The printing time is different from 🧑💻22020- 11- 15 11:35:33.180177+0800 Simple_iOS[57531:9389592] 🏃 ♀ ️ < NSThread:0x6000033bba80>{number = 7, name = (null)} param: 0x6000024e42e0⬅️ NSThread is a child thread. See yes3Seconds after printingCopy the code
  • The class method using NSThread explicitly creates the thread and automatically starts it immediately (no need to call it againstartFunction).
- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.
    
    // Create an object as an argument to the thread function. See that the run function prints the address of the object exactly the same
    NSObject *objc = [[TEMP alloc] init];
    NSLog(@"objc: %p", objc);
    
    // The function name begins with detach, reminiscent of the thread separation in Pthreads above.
    
    // The task performed by the thread takes the form of selector
    [NSThread detachNewThreadSelector:@selector(run:) toTarget:self withObject:objc];
    // (does not block the current thread, does not wait for the run function to complete execution before executing the following instruction)
    
    // Threads execute tasks in blocks, which are more compact
    [NSThread detachNewThreadWithBlock:^{
        sleep(2);
        NSLog(@"🙆 ‍ came ️ % @", [NSThread currentThread]);
    }];
    // (does not block the current thread, does not wait for the block to complete before executing the following instruction)
    
    NSLog(@"🧑 ‍ 💻 % @", [NSThread currentThread]);
}

- (void)run:(NSObject *)param {
    sleep(3);
    NSLog(@"🏃 ‍ ♀ ️ % @ param: % p", [NSThread currentThread], param);
}

/ / print:
2020- 11- 15 11:47:15.823097+0800 Simple_iOS[57662:9395974] objc: 0x600003904690
2020- 11- 15 11:47:15.823579+0800 Simple_iOS[57662:9395974] 🧑 💻 < NSThread:0x600002e0cb40>{number = 1, name = main} ⬅️ NSThread is the main thread2020- 11- 15 11:47:17.825466+0800 Simple_iOS[57662:9396067] 🙆 came ️ < NSThread:0x600002eb2140>{number = 9, name = (null)} ⬅️ NSThread is a child thread. The printing time is different from 🧑💻22020- 11- 15 11:47:18.828209+0800 Simple_iOS[57662:9396066] 🏃 ♀ ️ < NSThread:0x600002e0ce40>{number = 7, name = (null)} param: 0x600003904690⬅️ NSThread is a child thread. See yes3Seconds after printing2020- 11- 15 11:47:18.828532+0800 Simple_iOS[57662:9396066[🍀🍀🍀 TEMP dealloc ⬅️] 🍀🍀🍀 TEMP dealloc ⬅️ [🍀🍀🍀 TEMP dealloc ⬅️] 🍀🍀🍀 TEMP dealloc ⬅️ [🍀🍀🍀 TEMP dealloc ⬅️Copy the code
  • Implicitly create and start a thread.
- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.
    
    // Create an object as an argument to the thread function. See that the run function prints the address of the object exactly the same
    NSObject *objc = [[TEMP alloc] init];
    NSLog(@"objc: %p", objc);
    
    [self performSelectorInBackground:@selector(run:) withObject:objc];
    // (does not block the current thread, does not wait for the run function to complete execution before executing the following instruction)
    
    NSLog(@"🧑 ‍ 💻 % @", [NSThread currentThread]);
}

- (void)run:(NSObject *)param {
    sleep(3);
    NSLog(@"🏃 ‍ ♀ ️ % @ param: % p", [NSThread currentThread], param);
}

/ / print:
2020- 11- 15 12:12:34.071785+0800 Simple_iOS[57919:9413855] objc: 0x6000036648f0
2020- 11- 15 12:12:34.072102+0800 Simple_iOS[57919:9413855] 🧑 💻 < NSThread:0x60000217d040>{number = 1, name = main} ⬅️ NSThread is the main thread2020- 11- 15 12:12:37.431160+0800 Simple_iOS[57919:9414023] 🏃 ♀ ️ < NSThread:0x6000021132c0>{number = 6, name = (null)} param: 0x6000036648f0⬅️ NSThread is a child thread. See yes3Seconds after printing2020- 11- 15 12:12:37.431515+0800 Simple_iOS[57919:9414023[🍀🍀🍀 TEMP dealloc ⬅️] 🍀🍀🍀 TEMP dealloc ⬅️ [🍀🍀🍀 TEMP dealloc ⬅️] 🍀🍀🍀 TEMP dealloc ⬅️ [🍀🍀🍀 TEMP dealloc ⬅️Copy the code

This is how an NSThread creates a thread. Only threads created using the ALLOc and init methods of an NSThread return a specific thread instance. You use the first method to create a thread, but this method requires manually starting the thread using the start method.

NSThread. H file API analysis

The attributes or functions in the nsThread. h file are analyzed below.

currentThread

@property (class, readonly, strong) NSThread *currentThread;
Copy the code

Here class is really shocked to see this…. This… Is this a class attribute? After years of iOS development, I noticed for the first time that I could add a class to this property modifier, and that this [NSThread currentThread], which is used almost every day to determine the currentThread, never thought it was a class property, always thought it was a class function, Crying.

The return value represents the thread object of the currently executing thread. (The return value represents the thread object of the currently executing thread.)

isMultiThreaded

+ (BOOL)isMultiThreaded;
Copy the code

This function, encountered for the first time, is specific to the current application and returns whether the application is multithreaded. The main function in the main.m file returns 0, 1 if a thread is created using pthread_CREATE, and 1 directly in the viewDidLoad function.

Return value YES if the application is multithreaded, NO otherwise.

Official documentation explanation is: if you use detachNewThreadSelector: toTarget: withObject: separation or start from the main thread of the thread, argues that the application is multithreaded. If threads are separated in your application using non-Cocoa apis (such as POSIX or Multiprocessing Services apis), this method may still return NO. Separate threads do not have to be currently running for the application to be considered multithreaded — this method only indicates whether a single thread has been generated.

threadDictionary

@property (readonly, retain) NSMutableDictionary *threadDictionary;

// An example
// Add the following code to the run function of the above example code:

NSLog(@"🏃 ‍ ♀ ️ % @", [[NSThread currentThread] threadDictionary]);

NSMutableDictionary *dic = [[NSThread currentThread] threadDictionary];
[dic setObject:param forKey:@"KEY"];

NSLog(@"🏃 ‍ ♀ ️ % @", [[NSThread currentThread] threadDictionary]);

// Console print:
2020- 11- 15 15:12:58.245199+0800 Simple_iOS[59662:9494051] 🏃 ♀ ️ < NSThread:0x600001c244c0>{number = 7, name = (null)} param: 0x600000baaf90
2020- 11- 15 15:12:58.245562+0800 Simple_iOS[59662:9494051] 🏃 ♀ ️ {}2020- 11- 15 15:12:58.245928+0800 Simple_iOS[59662:9494051] 🏃♀️ {KEY ="<TEMP: 0x600000baaf90>"; ⬅️ saves param in threadDictionary}Copy the code

Get a thread’s read-only threadDictionary into which we can store our own content.

You can use the returned dictionary to store thread-specific data. The thread dictionary is never used during any operation on the NSThread object, it’s just a place to store any interesting data. For example, Foundation uses it to store default NSConnection and NSAssertionHandler instances for threads. You can define your own keys for the dictionary.

sleepUntilDate:

+ (void)sleepUntilDate:(NSDate *)date;
Copy the code

Block the current thread until the specified time. The date parameter also indicates the time for the thread to resume processing. No runloop processing occurs when a thread is blocked.

sleepForTimeInterval:

+ (void)sleepForTimeInterval:(NSTimeInterval)ti;
Copy the code

Hibernates a thread for a given time interval.

exit

+ (void)exit;
Copy the code

Terminates the current thread.

This method is used on the currentThread accessed by the currentThread class method. Before exit the thread, this method will NSThreadWillExitNotification and exit thread is sent to the default notification center. Because notice is synchronous transmission, so the thread can ensure all observers NSThreadWillExitNotification exit before receiving the notice. You should avoid calling this method because it does not give the thread an opportunity to clean up any resources allocated during execution.

threadPriority

+ (double)threadPriority;
Copy the code

Returns the priority of the current thread. The return value represents the priority of the current thread, specified as a floating point number between 0.0 and 1.0, where 1.0 is the highest priority.

The priorities in this range map to the priority values of the operating system. The “typical” thread priority might be 0.5, but since the priority is determined by the kernel, there is no guarantee that this value is actually what it is.

setThreadPriority:

+ (BOOL)setThreadPriority:(double)p;
Copy the code

Sets the priority of the current thread. If the priority is assigned successfully, YES; otherwise, NO. P uses the new priority specified by floating point numbers from 0.0 to 1.0, where 1.0 is the highest priority.

threadPriority

@property double threadPriority API_AVAILABLE(macos(10.6), ios(4.0), watchos(2.0), tvos(9.0)); // To be deprecated; use qualityOfService below
Copy the code

The priority of the receiver. The priority of a thread, specified by a floating point number between 0.0 and 1.0, where 1.0 is the highest priority. The priorities in this range map to the priority values of the operating system. The “typical” thread priority might be 0.5, but since the priority is determined by the kernel, there is no guarantee that this value is actually what it is.

qualityOfService

@property NSQualityOfService qualityOfService API_AVAILABLE(macos(10.10), ios(8.0), watchos(2.0), tvos(9.0)); // read-only after the thread is started
Copy the code

callStackReturnAddresses

@property (class, readonly, copy) NSArray<NSNumber *> *callStackReturnAddresses API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0));
Copy the code

Returns an array containing the return addresses of the call stack. An array containing the return addresses of the call stack. Each element is an NSNumber object containing an NSUInteger value.

callStackSymbols

@property (class, readonly, copy) NSArray<NSString *> *callStackSymbols API_AVAILABLE(macos(10.6), ios(4.0), watchos(2.0), tvos(9.0));
Copy the code

Returns an array containing call stack symbols. The return value is an array containing call stack symbols. Each element is an NSString object whose value format is determined by the backtrace_symbols() function. For more information, see the Backtrace_Symbols (3) macOS Developer tools man page.

The return value describes the call stack traceback of the current thread when this method was called.

name

@property (nullable, copy) NSString *name API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0));
Copy the code

The name of the thread.

stackSize

@property NSUInteger stackSize API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0));
Copy the code

The stack size of a thread, in bytes. The value must be in bytes and a multiple of 4KB. To change the stack size, you must set this property before starting the thread. Setting the stackSize after the thread is started changes the property size (as reflected by the stackSize method), but does not affect the actual number of pages reserved for the thread.

isMainThread

@property (readonly) BOOL isMainThread API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0));
Copy the code

A Boolean value indicating whether the receiver is a master thread. YES if the receiver is the main thread, NO otherwise.

isMainThread

@property (class, readonly) BOOL isMainThread API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0)); // reports whether current thread is main
Copy the code

Returns a Boolean value indicating whether the current thread is the master thread. YES if the current thread is the main thread, NO otherwise.

mainThread

@property (class, readonly, strong) NSThread *mainThread API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0));
Copy the code

Returns the NSThread object representing the main thread.

executing

@property (readonly, getter=isExecuting) BOOL executing API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0));
Copy the code

A Boolean value indicating whether the receiver (thread) is executing. YES if the receiver is executing, NO otherwise.

finished

@property (readonly, getter=isFinished) BOOL finished API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0));
Copy the code

A Boolean value indicating whether the receiver has completed execution. YES if the receiver completes execution, NO otherwise.

cancelled

@property (readonly, getter=isCancelled) BOOL cancelled API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0));
Copy the code

A Boolean value indicating whether the receiver cancels. YES if the receiver has been cancelled, NO otherwise. If your thread supports cancellation, it should periodically check this property and exit when it returns YES.

cancel

- (void)cancel API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0));
Copy the code

Change the cancel status of the receiver to indicate that it should exit (identify the cancel status, not perform the cancel operation).

The semantics of this method are the same as those used for NSOperation. This method sets state information in the receiver, which is then reflected by the Canceled property. Threads that support cancellation should periodically call the canceled method to determine whether the thread is actually canceled or exit if it has been identified as canceled. For more information about cancellations and operation objects, see NSOperation, which is covered in the next article.

start

- (void)start API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0));
Copy the code

Start the thread. (If you add [thread start] to [thread cancel], the thread will not execute.)

This method asynchronously generates a new thread and calls the receiver’s main method on the new thread. The executing property returns YES once the thread has started executing, which may happen after the start method returns.

If a receiver (an NSThread object) is initialized with target and selector, the default main method automatically calls that selector.

If this thread is the application of separation of the first thread, this method will object to nil NSWillBecomeMultiThreadedNotification notice issued to the default notification center.

main

- (void)main API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0));    // thread body method
Copy the code

The main entry point routine for a thread.

The default implementation of this method takes the target and selector used to initialize the NSThread and calls the selector at the specified target. If you subclass NSThread, you can override this method and use it to implement the main body of the thread. If you do, you do not need to call super.

You should never call this method directly. You should always start the thread by calling the start method.

At this point, we have seen all the code of the NSThread class, which is pretty clear.

NSObject + NSThreadPerformAdditions

Here is a taxonomy of NSObject, and the last part of the nsThread.h file. Some of them perform tasks in the main thread, specified thread, and background thread. ⛽ ️ ⛽ ️

performSelectorOnMainThread:withObject:waitUntilDone:modes:

- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(nullable id)arg waitUntilDone:(BOOL)wait modes:(nullable NSArray<NSString *> *)array;
Copy the code

Calls the recipient’s method on the main thread using the specified pattern.

ASelector: aSelector that identifies the method to call. The method should have no obvious return value and should take a single argument of type ID or no argument.

Arg: Arguments passed to aSelector when called. If the method does not accept arguments, nil is passed.

Wait: A Boolean value that specifies whether the current thread blocks after executing the specified selector on the sink on the main thread. Specifies YES to block the thread; Otherwise, specify NO for this method to return immediately. If the current thread is also the main thread and you pass YES, the message is executed immediately, otherwise it will be queued up for the next run through runloop.

Array: An array of strings identifying the mode that allows the specified selector to be executed. The array must contain at least one string. If you specify nil or an empty array for this argument, this method returns without executing the specified selector.

You can use this method to pass messages to the main thread of your application. The main thread contains the application’s main runloop and is where the NSApplication object receives events. In this case, the message is the method of the current object that you want to execute on the thread.

This method queues messages in the runloop of the main thread using the runloop mode specified in the array parameter. As part of its normal Runloop processing, the main thread queues the message (assuming it is running in one of the specified modes) and invokes the desired method. Assuming that the associated Runloop pattern is the same for each selector, multiple calls to the method from the same thread will cause the corresponding selectors to be queued and executed in the same order as the calls. If you specify a different mode for each selector, all selectors whose association mode does not match the current Runloop mode will be skipped until the runloop is subsequently executed in that mode.

You cannot unqueue messages using this method. If you want to cancel the current thread messages on the options, you must use the performSelector: withObject: afterDelay: or performSelector: withObject: afterDelay: inModes: method.

This method registers with the runloop of its current context and relies on runloops that run periodically for proper execution. A common context is when the Dispatch Queue is called, which may call this method and end up registering with runloops that do not run automatically on a regular basis. If you need this functionality when running on a Dispatch queue, you should use dispatch_after and related methods to get the desired behavior.

performSelectorOnMainThread:withObject:waitUntilDone:

- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(nullable id)arg waitUntilDone:(BOOL)wait;
// equivalent to the first method with kCFRunLoopCommonModes
Copy the code

The receiver’s methods are called on the main thread using the default mode (kCFRunLoopCommonModes).

performSelector:onThread:withObject:waitUntilDone:modes:

- (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(nullable id)arg waitUntilDone:(BOOL)wait modes:(nullable NSArray<NSString *> *)array API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0));
Copy the code

Calls the method of the receiver (any object of NSObject or its subclasses) on the specified thread using the specified mode.

performSelector:onThread:withObject:waitUntilDone:

- (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(nullable id)arg waitUntilDone:(BOOL)wait API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0));
// equivalent to the first method with kCFRunLoopCommonModes
Copy the code

Call the receiver’s methods on the specified thread using the default mode (kCFRunLoopCommonModes).

performSelectorInBackground:withObject:

- (void)performSelectorInBackground:(SEL)aSelector withObject:(nullable id)arg API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0));
Copy the code

Call the receiver’s method on the new background thread.

ASelector: aSelector that identifies the method to call. The method should have no obvious return value and should take a single argument of type ID or no argument.

Arg: Arguments passed to a method when called. If the method does not accept arguments, nil is passed.

This method creates a new thread in your application, or puts it in multithreaded mode if it has not already been done so. Methods represented by aSelector must set up the thread environment just as any other new thread is created in the program.

Refer to the link

Reference link :🔗

  • Pthread – Baidu Encyclopedia entry
  • Pthread_create – Baidu Encyclopedia entry
  • Pthread_cancel – Baidu Encyclopedia entry
  • Using threads in Linux
  • Introduction to the thread attribute pthread_attr_t
  • IOS multithreading: a detailed summary of “pthread, NSThread”
  • IOS Multithreading series — pThread
  • Summary of iOS basic principles – PThreads
  • C language multithreaded pthread library related functions
  • One of the actual schemes in iOS multithreading is pThread
  • IOS — Multithreading implementation Scheme 1 (PThread, NSThread)