Contents of this article

  • Pthreads base meaning
  • Pthreads thread definition and use
  • Basic definitions of locks (mutexes, spinlocks, read-write locks, conditional locks)
  • Pthreads USES
  • Use of locks (mutex, spin, read/write, conditional)

Pthreads

POSIX Threads (Pthreads for short) is the POSIX standard for threads. The standard defines a set of apis for creating and manipulating threads. On UniX-like operating systems (Unix, Linux, Mac OS X, etc.), Pthreads are used as operating system threads. There is also a port version of PThreads-Win32 for Windows.

POSIX Threads (English: POSIX Threads, often shortened to Pthreads) is the POSIX threading standard that defines a set of apis for creating and manipulating Threads. The library that implements the POSIX threading standard is often called Pthreads and is commonly used on UNIX-like POSIX systems such as Linux and Solaris. But implementations on Microsoft Windows also exist, such as pThreads-W32, a third-party library implemented directly using the Windows API; With the Windows SFU/SUA subsystem, you can use some of the native POSIX apis provided by Microsoft.

In simple terms, the operating system level is used by the thread, based on C language implementation, the use of difficult, need to manually manage the thread life cycle, the following is some basic use code.

Pthreads common functions and functions

Import the header file before using the function

#import <pthread.h>
Copy the code

A pthread_t.

Pthread_t is used to represent the Thread ID, which varies from implementation to implementation and can be a Structure, so it cannot be considered an integer.

2. Pthread_equal

The pthread_equal function is used to compare whether two pthread_t are equal.

int pthread_equal(pthread_t tid1, pthread_t tid2)
Copy the code

3. Pthread_self

The pthread_self function is used to get the thread ID of this thread

pthread _t pthread_self(void);
Copy the code

Create a thread

The pthread_create function is used to create a thread

int pthread_create(pthread_t *restrict tidp, const pthread_attr_t *restrict attr, 
Copy the code
  1. Create function parsing
    void *(*start_rtn)(void *), void *restrict arg); Pthread_t * RESTRICT tidp: Returns the ID of the last created Thread const pthread_attr_t * RESTRICT attr: Void *(*start_rtn)(void *) : Specifies a thread function pointer that returns a void* and also limits arg to void* void* RESTRICT: The argument passed to the thread function returns an error value.Copy the code
  2. The pthread function does not set errno when an error occurs, but returns the error value directly
  3. Under Linux systems, in older kernels, threads are also considered special processes that share address space and resources, so different threads created in the same Process have different Process ids (obtained by calling getPID). In the new 2.6 kernel, Linux uses the NPTL(Native POSIX Thread Library) Thread model (see here and here), under which different threads in the same process call getPID to return the same PID.
  4. Make any assumptions about the order in which the new thread is created and the current creator thread will run

Terminate the thread

  1. Exit, _Exit, _Exit is used to terminate the current process, not the thread

  2. There are three ways to abort a thread:

    A. Return in a thread function

    B. Cancelled by another thread in the same process

    C. The thread calls the pthread_exit function

  3. Pthread_exit and pthread_join functions

    A. Thread A calls pthread_JOIN (B,&rval_ptr), gets blocked, and goes into the Detached state. (If the state is Detached, the pthread_join returns EINVAL.) Rval_ptr can pass NULL if the end code of B is not of interest.

    B. Thread B calls pthread_exit(rval_ptr) and exits thread B with rval_ptr. Note that rval_ptr points to the memory lifecycle and should not point to data in B’s Stack.

    C. Thread A resumes running, the pthread_join call terminates, and thread B’s terminating code is saved in the rval_ptr argument. If thread B is cancelled, the value of rval_ptr is PTHREAD_CANCELLED.

    The two functions are prototyped as follows

    void pthread_exit(void *rval_ptr);
    int pthread_join(pthread_t thread, void **rval_ptr);
    Copy the code
  4. A Thread can ask another Thread to Cancel by calling the pthread_cancel function:

    void pthread_cancel(pthread_t tid)

    This causes the specified thread to be cancelled as if pthread_exit(PTHREAD_CANCELLED) had been called. However, the specified thread can choose to ignore or do its own processing, as discussed later. In addition, this function does not cause a Block, but simply issues the request Cancel.

  5. A thread can schedule some function to be called automatically when it exits, like the atexit() function. The following functions need to be called:

    void pthread_cleanup_push(void (*rtn)(void *), void *arg); void pthread_cleanup_pop(int execute);

    These two functions maintain a Stack of function Pointers that can be pushed /pop with function parameter values. The order of execution is from the top of the stack to the bottom of the stack, which is the reverse of push.

    The thread cleanup Handlers specified by pthread_cleanup_push are called in the following case:

    A. call pthread_exit

    B. Cancel the request accordingly

    C. Call pthread_cleanup_pop() with a non-zero argument. (If pthread_cleanup_POP () is called with 0, the handler will not be called

    One of the odder requirements is that, since the two functions may be implemented in macro mode, the calls to the two functions must be within the same Scope and paired, since the pthread_cleanup_push implementation may have a {, Pthread_cleanup_pop might have an}. Therefore, in general, these two functions are used to handle unexpected situations, as shown in the following example:

    Void *thread_func(void *arg) {pthread_cleanup_push(cleanup, "handler") //do something
       Pthread_cleanup_pop(0);
        return(0) (void *); }Copy the code
  6. Pending state

    By default, the exit status of thread A is saved until pthread_join has been called for that thread. That is, even if thread A has terminated, A’s exit status is saved as long as no thread B calls pthread_JOIN (A). In the Detached state, the thread can be recycled as soon as it quits, and the Detached state is lost. In this state, the pthread_join function cannot be called for this thread. We can put a specified thread into Detach state by calling the pthread_detach function:

    int pthread_detach(pthread_t tid);
    Copy the code

    By modifying the attr argument that calls the pthread_create function, you can specify that a thread goes into the Detached state as soon as it is created

  7. The correlation between process and thread functions

Process Primitive Thread Primitive Description
fork pthread_create Create a new control flow
exit pthread_exit Exit the existing control flow
waitpid pthread_join Wait for the control flow and get the end code
atexit pthread_cleanup_push Registers functions that are called when control flow exits
getpid pthread_self Gets the ID of the control flow
abort pthread_cancel Request an abnormal exit

6. The lock

  1. Mutex: pthread_mutex_

    A. Used for mutually exclusive access

    B. Type: pthread_mutex_t, which must be initialized as PTHREAD_MUTEX_INITIALIZER (mutex for static allocation, equivalent to pthread_mutex_init(… , NULL)) or call pthread_mutex_init. Mutex should also be destroyed with pthread_mutex_destroy. The two functions are prototyped as follows:

    int pthread_mutex_init(
        pthread_mutex_t *restrict mutex,
        const pthread_mutexattr_t *restrict attr)
    int pthread_mutex_destroy(pthread_mutex_t *mutex);
    Copy the code

    C. thread_mutex_lock Is used to Lock Mutex. If the Mutex has been locked, the call blocks until the Mutex is unlocked, then the call locks the Mutex and returns. Pthread_mutex_trylock is similar, except that Mutex does not Block when locked and returns an error value, EBUSY. Pthread_mutex_unlock is an unlock mutex. The three functions are prototyped as follows:

    int pthread_mutex_lock(pthread_mutex_t *mutex);
    int pthread_mutex_trylock(pthread_mutex_t *mutex);
    int pthread_mutex_unlock(pthread_mutex_t *mutex);
    Copy the code
  2. Spin lock: Spin lock

    The first thing to mention is that OSSpinLock has been bugged and is not completely thread-safe.

    In the new iOS, the system maintains 5 different thread priorities /QoS: Background, Utility, Default, User-initiated, and User-interactive. High-priority threads always execute before low-priority threads, and a thread is not disturbed by threads of lower priority than itself. This thread scheduling algorithm breaks the Spin lock by creating potential priority inversion problems. Specifically, if a low-priority thread acquires a lock and accesses a shared resource, and a high-priority thread attempts to acquire the lock, it will be in a busy state of spin lock and thus consume a lot of CPU. The low-priority thread cannot compete with the high-priority thread for CPU time, resulting in the task being delayed and unable to release the lock. This isn’t just a theoretical problem, libobjc has encountered this problem so many times that apple engineers disabled OSSpinLock. Apple engineer Greg Parker mentioned that one solution to this problem is to use the truly unbounded backoff algorithm, which avoids the livelock problem, but if the system is heavily loaded, it may still block high-priority threads for tens of seconds. The alternative is to use the Handoff Lock algorithm, which libobJC is currently using. The lock holder stores the thread ID inside the lock, and the lock holder temporarily contributes its priority to avoid priority inversion. In theory this pattern can cause problems under more complex multi-lock conditions, but in practice so far all is well. OSSpinLock Spin lock, the highest performance lock. The principle is very simple, just do while waiting. The downside is that it consumes a lot of CPU resources when waiting, so it is not suitable for longer tasks. It is ideal for memory cache access. – from ibireme

  3. Read/write lock: pthread_rwlock_

    A. Multiple threads can obtain a reader-writer lock in read mode at the same time, but only one thread can obtain a reader-writer lock in write mode.

    B. Read/write locks have three states

    I. One or more threads acquire the read lock. Other threads cannot acquire the write lock. One thread acquires the write lock and no other thread acquires the read lock III. No thread acquires the write lockCopy the code

    C. The type is pthread_rwlock_T

    D. Perform the following operations to create and close the vm:

    int pthread_rwlock_init(
          pthread_rwlock_t *restrict rwlock,
          const pthread_rwlockattr_t *restrict attr)
    int pthread_rwlock_destroy(pthread_rwlock_t *rwlock);
    Copy the code

    E. Obtain read/write locks as follows:

    int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock); int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock); int pthread_rwlock_unlock(pthread_rwlock_t *rwlock); int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock); int pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock); Pthread_rwlock_rdlock: Obtains the read lock. Pthread_rwlock_wrlock: obtains the write lock. Pthread_rwlock_unlock: releases the lockCopy the code

    Pthread_rwlock_rdlock and pthread_rwLOCK_WRLOCK and Pthread_rwlock_UNLOCK do not need to be checked. If we code it right.

  4. Conditional lock: Conditional Variable

    If a thread has to wait for a condition to continue, and that condition is generated by another thread, then using locks alone is a bit of a stretch. Either constantly polling, consumption of resources, or every period of time to query, loss of timeliness. Condition variables are designed to satisfy this scenario, allowing a thread to wait for a condition and be notified when the condition is met. Multithreaded races can also occur while fetching condition variables and waiting for them to occur, so condition variables often work with mutex.

    A. Conditions must be protected by Mutex

    B. The type is pthread_cond_t, which must be initialized as PTHREAD_COND_INITIALIZER (used for static allocation, equivalent to pthread_cond_init(… NULL)) or call pthread_cond_init

    int pthread_cond_init(
          pthread_cond_t *restrict cond,
          const pthread_condxattr_t *restrict attr)
    int pthread_cond_destroy(pthread_cond_t *cond);
    Copy the code

    c. pthread_cond_wait

    The function waits for the condition to occur (=true). Pthread_cond_timedwait is similar, but returns an error value ETIMEDOUT if the wait times out. The timeout period is specified using the timespec structure. In addition, both functions need to pass in a Mutex for the protection condition

    int pthread_cond_wait(
           pthread_cond_t *restrict cond,
           pthread_mutex_t *restrict mutex);
    int pthread_cond_timedwait(
           pthread_cond_t *restrict cond,
           pthread_mutex_t *restrict mutex,
           const struct timespec *restrict timeout);
    Copy the code

    D. timespec structure is defined as follows:

    struct timespec {
        time_t tv_sec;       /* seconds */
        long   tv_nsec;      /* nanoseconds */
    };
    Copy the code

    Note that the timeof timespec is absolute time, not relative time, so you need to call getTimeofday to get the current time, and then convert it to timespec structure, plus the offset.

    E. Two functions are used to inform the thread that the condition is met (=true) :

    int pthread_cond_signal(pthread_cond_t *cond);
    int pthread_cond_broadcast(pthread_cond_t *cond);
    Copy the code

    The difference is that the former wakes up a single thread, while the latter wakes up multiple threads

Pthreads usage method

First include the header file #import 2. Basic use test code

- (void)viewDidLoad { [super viewDidLoad]; // Create thread -- define a pthread_t thread; /* pthread_create(&thread, NULL, run, NULL); The meanings of the parameters in The first argument &thread is the thread object and the second and fourth arguments are thread properties, */ pthread_create(&thread, NULL, threadStart, NULL); Void *threadStart (void *data) {NSLog(@)"La la la la la la multithreading test ~:%@",[NSThread currentThread]);
    return NULL;
}
Copy the code

End the current holding thread

Call pthread_detach(thread) at the end of the task; pthread_cancel(thread); Or pthread_exit ()Copy the code
  1. The use of Mutex

    A. Create a lock

    The mutex is the structure of pthread_mutex_t, and the macro is a structural constant. The locks can be initialized statically as follows:

    pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
    Copy the code

    In addition, locks can be created dynamically using the pthread_mutex_init function, which is modeled as follows:

    int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t * attr)
    Copy the code

    B. Lock attributes

    The mutex attribute can be specified by pthread_mutexattr_init(pthread_mutexattr_t *mattr); Can then call other property setting methods to set its properties. Scope of mutex: You can specify synchronization between this process and other processes or between different threads within the same process. PTHREAD_PROCESS_SHARE and PTHREAD_PROCESS_PRIVATE can be set. The default is the latter, indicating intra-process locking. You can use

    int pthread_mutexattr_setpshared(pthread_mutexattr_t *mattr, int pshared)
    pthread_mutexattr_getshared(pthread_mutexattr_t *mattr,int *pshared)
    Copy the code

    Used to set and obtain the lock range.

    C. Types of mutex:

    PTHREAD_MUTEX_TIMED_NP, which is the default, is a normal lock. When a thread locks, the rest of the threads will form a waiting queue and get the lock according to the priority after unlocking. This locking strategy ensures the fairness of resource allocation. PTHREAD_MUTEX_RECURSIVE_NP, nested lock that allows the same thread to successfully acquire the same lock multiple times and unlock it multiple times. If the request is made by a different thread, the race is re-run when the locked thread is unlocked. PTHREAD_MUTEX_ERRORCHECK_NP, error check lock, if the same thread requests the same lock, return EDEADLK, otherwise same as PTHREAD_MUTEX_TIMED_NP action. This ensures that deadlocks do not occur in the simplest case when multiple locks are not allowed. PTHREAD_MUTEX_ADAPTIVE_NP: the simplest type of lock that can be used to adapt to a lock. Pthread_mutexattr_settype (pthread_mutexattr_t *attr, Int type) pthread_mutexattr_getType (pthread_mutexattr_t *attr, int *type) Gets or sets the type of the lock.

    D. Lock release

    With a call to pthread_mutex_destory, resources occupied by the lock can be released, but only if the previous lock is not currently locked.

    E. the lock operation

    Lock operations include pthread_mutex_lock(), pthread_mutex_unlock(), and test pthread_mutex_trylock().

    int pthread_mutex_lock(pthread_mutex_t *mutex)
    int pthread_mutex_unlock(pthread_mutex_t *mutex)
    int pthread_mutex_trylock(pthread_mutex_t *mutex)
    Copy the code

    Pthread_mutex_trylock () has similar semantics to pthread_mutex_lock(), except that EBUSY is returned when the lock is already occupied rather than pending

    F. code

    Commonly used lock

    __block pthread_mutex_t theLock;
    pthread_mutex_init(&theLock, NULL); 
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        pthread_mutex_lock(&theLock);
        NSLog(@"Operation 1 requiring thread synchronization begins");
        sleep(3);
        NSLog(@"Operation 1 requiring thread synchronization ends");
        pthread_mutex_unlock(&theLock);
        
    });
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        sleep(1);
        pthread_mutex_lock(&theLock);
        NSLog(@"Operation requiring thread synchronization 2");
        pthread_mutex_unlock(&theLock);
        });
    Copy the code

    Recursive locks (NSRecursiveLock)

    __block pthread_mutex_t theLock; // pthread_mutex_init(&theLock, NULL); Pthread_mutexattr_t attr; pthread_mutexattr_init(&attr); pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); pthread_mutex_init(&theLock, &attr); // Create pthread_mutexattr_destroy(&attr); dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ static void (^RecursiveMethod)(int); RecursiveMethod = ^(int value) { pthread_mutex_lock(&theLock);if (value > 0) {
                NSLog(@"value = %d", value);
                sleep(1);
                RecursiveMethod(value - 1);
            }
            pthread_mutex_unlock(&theLock);
        };
        RecursiveMethod(5);
    });
    Copy the code
  2. The use of Spin locks

  • Spin locks are similar to mutex
  • But here’s the difference: spinlocks are non-blocking. When a thread is unable to acquire a spinlock, it spins until the lock is released, and the thread does not hang while waiting. (Essentially, if the spinlock has been held by another execution unit, the caller will loop around waiting for the holding of the spinlock to release the lock).
  • Users of spin locks tend to hold them for a short period of time, when they are much more efficient than mutex.
  • The spin-lock is preempted during hold
    Static OSSpinLock myLock = OS_SPINLOCK_INIT; -(void)SpinLockTest{OSSpinLockLock(&myLock); // todo something
        OSSpinLockUnlock(&myLock);
    }
    Copy the code

OSSpinLock is very efficient, but in January 2016 it was discovered that there was a priority inversion problem.

  1. pthread_con_Conditional locking in iOS, conditional locking is implemented in two ways:

A. the POSIX mode

POSIX provides the following functions:

  • Pthread_cond_init initialization
  • Pthread_cond_wait Wait condition
  • Pthread_cond_broadcast Sends a broadcast to wake up all waiting threads
  • Pthread_cond_signal sends a signal to wake up the first thread
  • Pthread_cond_destroy destroyed

POSIX instance

Like mutex, condition variables can be created in either static or dynamic mode. The static mode uses the PTHREAD_COND_INITIALIZER constant as follows: Pthread_cond_t cond=PTHREAD_COND_INITIALIZER Calls pthread_cond_init() dynamically. The API is defined as follows:  int pthread_cond_init(pthread_cond_t *cond, pthread_condattr_t *cond_attr)Copy the code

Although attributes for conditional variables are defined in the POSIX standard, they are not implemented in LinuxThreads, so the cond_attr value is usually NULL and ignored.

To deregister a condition variable, call pthread_cond_destroy(), which can only be deregister if there are no threads waiting on the condition variable, otherwise return EBUSY. Because Linux implements condition variables that allocate few resources, the logout action simply involves checking if there are waiting threads.

There are two ways to wait for conditions: Pthread_cond_wait () and timedwait pthread_cond_timedwait(), where if conditions are not met before a given time, ETIMEOUT is returned to end the wait. Where the absTime takes the form of absolute time in the same sense as the time() system call, with 0 representing 0 minutes 0 seconds GMT on January 1, 1970. The API is as follows:

int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex) 
int pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex, const struct timespec *abstime)
Copy the code

Either type of wait must be combined with a mutex to prevent multiple threads from simultaneously requesting the Race Condition of pthread_cond_wait() (or pthread_cond_timedwait(), the same below). Mutex mutex must be either a normal lock (PTHREAD_MUTEX_TIMED_NP) or an adaptive lock (PTHREAD_MUTEX_ADAPTIVE_NP), It must be locked by the thread (pthread_mutex_lock()) before calling pthread_cond_wait(), while mutex remains locked until the conditional wait queue is updated and unlocked before the thread suspends and enters the wait. Before the condition is met to leave pthread_cond_wait(), mutex will be re-locked to match the action taken before entering pthread_cond_wait().

Pthread_cond_signal () activates a thread waiting for the condition. If there are multiple waiting threads, one of them is activated in the order of enqueue. Pthread_cond_broadcast () activates all wait threads.

Here is a classic mini program found on the Internet:

Initialization code:

static pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER;
static pthread_cond_t cond = PTHREAD_COND_INITIALIZER;

  struct node {
    int n_number;
    struct node *n_next;
} *head = NULL;

  - (void)viewDidLoad {
    [super viewDidLoad];
    self.view.backgroundColor = [UIColor whiteColor];
    self.title = @"Pthread_Con_ViewController";
    
    UIButton *startChildButton = [UIButton buttonWithType:UIButtonTypeSystem];
    startChildButton.frame = CGRectMake(0, 300, 200, 40);
    [startChildButton setTitle:@"Enable A Test (POSIX)" forState:UIControlStateNormal];
    [startChildButton setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
    [startChildButton addTarget:self action:@selector(aButtonClick:) forControlEvents:UIControlEventTouchUpInside];
    [self.view addSubview:startChildButton];
}
Copy the code

Implementation code:

- (void)aButtonClick:(UIButton *)sender { pthread_t tid; int i; struct node *p; pthread_create(&tid, NULL, thread_func, NULL); /*[tx6-main]*/ Child threads wait for resources, like producers and consumers, but consumers can be multiple consumers instead of just supporting a single consumer. This model is simple but powerfulfor(i = 0; i < 10; i++) { p = malloc(sizeof(struct node)); p->n_number = i; pthread_mutex_lock(&mtx); P -> next = head; p-> next = head; head = p; pthread_cond_signal(&cond); pthread_mutex_unlock(&mtx); / / unlock sleep (1); }printf("thread 1 wanna end the line.So cancel thread 2./n"); pthread_cancel(tid); One extra note about pthread_cancel is that it terminates the child thread from outside, and the child thread exits at the nearest cancellation point, which in our code must be pthread_cond_wait(). pthread_join(tid, NULL);printf("All done -- exiting/n");
}

  // [thread_func]
static void cleanup_handler(void *arg)
{
    NSLog(@"Cleanup handler of second thread.");
    free(arg);
    (void)pthread_mutex_unlock(&mtx);
}
static void *thread_func(void *arg)
{
    struct node *p = NULL;
    
    pthread_cleanup_push(cleanup_handler, p);
    whilePthread_mutex_lock (&mtx); / / thiswhileIn particular, a single pthread_cond_wait function is perfect, so why have one herewhile(head == NULL) The thread in pthread_cond_wait can be woken up unexpectedly if the head! = NULL, which is not what we want. At this point, the thread should continue into pthread_cond_waitwhile(head == NULL) {// pthread_cond_wait removes MTX locked by pthread_mutex_lock, and then blocks and sleeps in the wait pair until it is woken up again. The process locks pthread_mutex_lock(& MTX); Pthread_cond_wait (&cond, &mtx); /*block-->unlock-->wait(a)return-->lock*/
        }
        p = head;
        head = head->n_next;
        NSLog(@"Got %d from front of queue/n",p->n_number); free(p); pthread_mutex_unlock(&mtx); } pthread_cleanup_POP (0);return 0;
}
Copy the code

B. NSCondition way

  • NSCondition: a combination of a mutex and a conditional lock. If a thread is blocked waiting for the signal, it can be woken up by another thread. Due to the differences in operating system implementation, the thread may be woken up even without sending the signal message. NSCondition apple has a tutorial on the Threading Programming Guide.
  • NSConditionLock: different from NSCondition, it obtains the lock when the condition is true and releases the lock when it is not.

NSCondition commonly used API:

[condition lock]; [condition unlock]; [condition unlock]; [condition unlock]; // use [condition with lockwait]; // make the current thread wait [condition signal]; // The CPU signals the thread to stop waiting and continue executingCopy the code

NSCondition test code:

NSCondition *condition = [[NSCondition alloc] init]; static int count = 0; // dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{while(count<20) { [condition lock]; // count ++; NSLog(@"Production = %d",count); [condition signal]; [condition unlock]; }}); // consumer dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{while(count>0) { [condition lock]; // consume count --; NSLog(@"Consumed surplus = %d",count); [condition unlock]; }});Copy the code

NSConditionLock conditionlock

Conditionlock - (id)initWithCondition:(NSInteger)condition - (NSInteger)condition 1, - (BOOL)lockBeforeDate:(NSDate *) 1, (BOOL)lockBeforeDate:(NSDate *)limit// Try to get the lock. 1, -- (void)lockWhenCondition:(NSInteger) Condition -- (BOOL)lockWhenCondition (NSInteger)condition beforeDate:(NSDate *)limit-- (BOOL)tryLock // If the condition of the receiving object is equal to the given condition, 5, -- (BOOL)tryLockWhenCondition:(NSInteger)condition 6, -- (void)unlockWithCondition:(NSInteger)conditionCopy the code

NSConditionLock conditionlock

NSConditionLock *condLock = [[NSConditionLock alloc] initWithCondition:0]; static int count = 0; // dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{while(true) { [condLock lock]; // count ++; NSLog(@"Production = %d",count);
            [condLock unlockWithCondition:(count >= 10 ? 10 : 0)];
            if (count >= 10) {
                break; } sleep(1); }}); // consumer dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{while (true) { [condLock lockWhenCondition:10]; // consume count --; NSLog(@"Consumed surplus = %d",count); [condLock unlockWithCondition:(count<=0 ? 0 : 10)]; sleep(1); }});Copy the code
  1. pthread_rwlock_Use of read/write locks A. Functions Read/write locks divide visitors into read and write. In read/write lock mode, all threads that attempt to access a resource in read/lock mode are granted access permission, and all threads that attempt to lock a resource in write lock mode are blocked until all read locks are released. When in write lock mode, all threads attempting to lock will block. When a read/write lock is held by a thread in read mode, the other threads of the write operation are blocked and the other threads of the read operation can continue. When a read-write lock is held by a thread in write mode, the other threads of the write operation are blocked, and the other threads of the read operation are also blocked.

B. create

pthread_rwlock_t rwlock;
Copy the code

C. the initialization

pthread_rwlock_init(&rwlock, NULL);
Copy the code

D. use

    UIButton *startChildButton = [UIButton buttonWithType:UIButtonTypeSystem];
    startChildButton.frame = CGRectMake(0, 300, 200, 40);
    [startChildButton setTitle:@"Open test A" forState:UIControlStateNormal];
    [startChildButton setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
    [startChildButton addTarget:self action:@selector(aButtonClick:) forControlEvents:UIControlEventTouchUpInside];
    [self.view addSubview:startChildButton];
    
    UIButton *endChildButton = [UIButton buttonWithType:UIButtonTypeSystem];
    endChildButton.frame = CGRectMake(0, 400, 200, 40);
    [endChildButton setTitle:@"Close B test" forState:UIControlStateNormal];
    [endChildButton setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
    [endChildButton addTarget:self action:@selector(bButtonClick:) forControlEvents:UIControlEventTouchUpInside];
    [self.view addSubview:endChildButton];
Copy the code
  - (void)aButtonClick:(UIButton *)sender {
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        [self readBookWithTag:0];
    });
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        [self readBookWithTag:1];
    });
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        [self readBookWithTag:2];
    });
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        [self writeBook:3];
    });
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        [self readBookWithTag:4];
    });
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        [self writeBook:5];
    });
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        [self readBookWithTag:6];
    });
}

  - (void)bButtonClick:(UIButton *)sender {
    __block int i;
  dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        i = 5;
        while (i>=0) {
            NSString *temp = [NSString stringWithFormat:@"%d", i]; [self writingLock:temp]; i--; }}); dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ i = 5;while(i>=0) { [self readingLock]; i--; }}); } - (void)readBookWithTag:(NSInteger )tag {
    pthread_rwlock_rdlock(&rwlock);
    // read
    NSLog(@"Read % tu",tag);
    sleep(3);
    pthread_rwlock_unlock(&rwlock);
}

  - (void)writeBook:(NSInteger)tag {
    pthread_rwlock_wrlock(&rwlock);
    // write
    NSLog(@"Writing % tu",tag);
    sleep(3);
    pthread_rwlock_unlock(&rwlock);
}

  -(void)writingLock:(NSString *)temp{
    pthread_rwlock_wrlock(&rwlock);
    // writing
    self.rwStr = temp;
    NSLog(@"Write = = % @", temp);
    sleep(1);
    pthread_rwlock_unlock(&rwlock);
  }

  -(NSString *)readingLock{
    pthread_rwlock_rdlock(&rwlock);
    // reading
    NSString *str = self.rwStr;
    NSLog(@"Read = = % @",self.rwStr);
    pthread_rwlock_unlock(&rwlock);
    return str;
  }
Copy the code

Above, is the Pthread survey results, aspirants, things have become, burn Bridges, one hundred and two Qin Pass belongs to Chu;

Good Samaritan, days, three thousand more hardships, can swallow Wu.