Summary of Linux multithreading and summary of linux multithreading

Source: Internet
Author: User
Tags exit in

Summary of Linux multithreading and summary of linux multithreading

A thread is the complete execution sequence of an independent task in a program, that is, a schedulable entity. A process is equivalent to an abstraction of a running program. Based on the identity of the scheduler in the running environment, threads can be divided into kernel threads and user threads. Kernel threads are called LWP (Light Weight Process, lightweight thread) in some systems. They run in the kernel space and are scheduled by the kernel. User threads run in the user space and are scheduled by the thread library. When a kernel thread of a process obtains the CPU usage right, it loads and runs a user thread. It can be seen that the kernel thread is equivalent to the 'Container' run by the user thread. A process can have M kernel threads and N user threads, where M <= N, in addition, the ratio of M to N is fixed in all processes of a system.

Thread control functions

Pthread_create

# Include <pthread. h> int pthread_create (pthread_t * tidp, const pthread_attr_t * attr, void * (* start_rtn) (void *), void * arg); // return: 0 is returned for success, error Return Error Code

When the pthread_create function returns success, the memory pointed to by tidp is set as the thread ID of the newly created thread. The type of pthread_t is defined:

#include <bits/pthreadtypes.h>typedef unsigned long int pthread_t;

The attr parameter is used to set various thread attributes. If it is NULL, it indicates the default thread attribute. The newly created thread starts running from the address of the start_rtn function. This function has only one parameter arg with no type pointer. If more than one parameter needs to be input to the start_rtn function, you can put the parameter into a structure, and then pass the address of this structure as the arg parameter.

When a thread is created, it cannot be guaranteed which thread will run first: whether it is a newly created thread or a calling thread. The newly created thread can access the address space of the calling process and inherit the floating point environment of the calling thread and the signal shielding characters. However, the pending Signal Set of the thread is cleared. So what is a pending signal? The signal is generated to the interval during which the signal is processed, which is called a pending signal.

Pthread_exit

# Include <pthread. h> void pthread_exit (void * rval_ptr); // thread termination

The thread should call this function at the end to ensure safe and clean exit. The pthread_exit function transmits exit information to the recycler of the calling thread through the rval_ptr parameter. Other threads in the process can call the pthread_join function to access this pointer. Pthread_exit will not return to the caller after execution, and will never fail.

A thread can exit in the following three ways to stop its control flow without terminating the entire process:

  • The thread only exits from the startup process. The returned value is the exit code of the thread.
  • The thread can be canceled by other threads in the same process.
  • The thread calls pthread_exit.

Pthread_join

# Include <pthread. h> int pthread_join (pthread_t thread, void ** rval_ptr); // return: 0 is returned for success, and error code is returned for Error

Thread is the identifier of the target thread. rval_ptr indicates the exit information returned by the target thread. This function will be blocked until the end of the thread to be recycled. Possible error codes:

Pthread_cancel

# Include <pthread. h> int pthread_cancel (pthread_t thread); // return: 0 is returned for success, and error code is returned for Error

By default, the pthread_cancel function will call the pthread_exit function with the thread ID,, the target thread receiving the cancellation request can decide whether to allow cancellation and how to cancel the cancellation, which are controlled by the following two functions:

#include <pthread.h>int pthread_setcancelstate(int state, int *oldstate);int pthread_setcanceltype(int type, int *oldstate);

Note that pthread_cancel does not wait for the thread to end. It only initiates a request.

 

Mutex

A mutex is essentially a lock. before accessing public resources, you can set a mutex (Lock) to ensure that only one thread accesses data at a time, and then release (unlock) after the access is complete) mutex. After mutex lock, other threads will be blocked when they try to lock the mutex again, knowing that the current thread releases the mutex lock. If more than one mutex is released, all threads that are blocked on the mutex will become runable. The first thread that becomes running can lock the mutex, when other threads see that the mutex volume is still locked, they can only block it again and wait for the mutex volume.

The mutex is represented by the pthread_mutex_t data type. before using the mutex, you must use the pthread_mutex_init function to initialize it. Note that you must call pthread_mutex_destroy after use.

# Include <pthread. h> int pthread_mutex_init (pthread_mutex_t * mutex, const pthread_mutexattr_t * mutexattr); int pthread_mutex_destroy (pthread_mutex_t * mutex); // the return value is 0. Otherwise, an error code is returned

Pthread_mutex_init is used to initialize the mutex lock. mutexattr is used to specify the mutex lock attribute. If it is NULL, it indicates the default attribute. In addition to using this function to initialize mutex, you can also use the following method to initialize: pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER.
Pthread_mutex_destroy is used to destroy the mutex lock to release the occupied kernel resources. Destroying a mutex lock that has been locked will lead to unpredictable consequences.

# Include <pthread. h> int pthread_mutex_lock (pthread_mutex_t * mutex); int aggregate (pthread_mutex_t * mutex

Pthread_mutex_lock locks a mutex with an atomic operation. If the target mutex lock has been locked, pthread_mutex_lock will be blocked until the mutex lock occupies the lock.
Pthread_mutex_trylock is similar to pthread_mutex_lock, but it always returns immediately. No matter whether the operated mutex lock is locked or not, it is the non-blocking version of pthread_mutex_lock. When the target mutex lock is not locked, pthread_mutex_trylock performs the lock operation. Otherwise, the EBUSY error code is returned. Note: The pthread_mutex_lock and pthread_mutex_trylock discussed here are for common locks. For other types of locks, the two locking functions have different behaviors.
Pthread_mutex_unlock unlocks a mutex in atomic mode. If another thread is waiting for this mutex lock, one of these threads will get it.

Examples of mutex lock usage:

/*** Use three threads to print a B C */# include <stdio. h> # include <stdlib. h> # include <string. h> # include <pthread. h> typedef struct ThreadInfo_t {char info;/* 'A' or 'B' or 'C' */int n;/* remainder num */int num; /* share num */pthread_mutex_t mutex;} ThreadInfo; void * func (void * arg) {int cnt = 3; ThreadInfo * info = (ThreadInfo *) arg; int result = info-> n; char show = info-> info; while (cnt> 0) {if (info-> num % 3 = result) {printf ("--- % c \ n", show); pthread_mutex_lock (& info-> mutex); info-> num ++; cnt --; pthread_mutex_unlock (& info-> mutex) ;}return NULL;} int main (int argc, char ** argv) {pthread_t t1, t2, t3; ThreadInfo info; memset (& info, 0, sizeof (ThreadInfo); pthread_mutex_init (& (info. mutex), NULL); info. n = 0; info.info = 'a'; pthread_create (& t1, NULL, func, & info); sleep (1); info. n = 1; info.info = 'B'; pthread_create (& t2, NULL, func, & info); sleep (1); info. n = 2; info.info = 'C'; pthread_create (& t3, NULL, func, & info); pthread_join (t1, NULL); pthread_join (t2, NULL ); pthread_join (t3, NULL); return 0 ;}

 

Read/write lock

The read/write lock is similar to the mutex, but the read/write lock has a higher concurrency. The mutex is either locked or unlocked, and only one thread can lock it at a time. The read/write locks can be in three states: Read mode and write mode. Only one thread can occupy the read/write lock in the write mode at a time, but multiple threads can simultaneously occupy the read/write lock in the Read mode. Read/write locks are suitable for reading data structures more frequently than writing data.

When the read/write lock is in the write lock status, all threads trying to lock the lock will be blocked before the lock is unlocked. When the read/write lock is in the read/write lock status, all threads that attempt to lock it in the Read mode can obtain access permission, but any thread that wants to lock the lock in the write mode will be blocked, until all threads release their read locks.

# Include <pthread. h> int pthread_rwlock_init (pthread_rwlock_t * restrict rwlock, const limit * restrict attr); int limit (Limit * restrict rwlock); // success returns 0; otherwise, the error code is returned.

Use pthread_rwlock_init to initialize the read/write lock. If you want the read/write lock to have a default attribute, you can pass a NULL pointer to attr. When no read/write locks are required, call pthread_rwlock_destroy for cleanup.

# Include <pthread. h> int round (pthread_rwlock_t * restrict rwlock); int round (pthread_rwlock_t * restrict rwlock

Read/write locks, write locks, and unlock operations.

Example of a read/write lock program:

/*** Two read threads read data, and one write thread updates data */# include <stdio. h> # include <stdlib. h> # include <string. h> # include <pthread. h> # define READ_THREAD 0 # define WRITE_THREAD 1int g_data = 0; pthread_rwlock_t g_rwlock; void * func (void * pdata) {int data = (int) pdata; while (1) {if (READ_THREAD = data) {pthread_rwlock_rdlock (& g_rwlock); printf ("----- % d ------ % d \ n", pthread_self (), g_data); sleep (1 ); pthread_rwlock_unlock (& g_rwlock); sleep (1);} else {pthread_rwlock_wrlock (& g_rwlock); g_data ++; printf ("add the g_data \ n "); pthread_rwlock_unlock (& g_rwlock); sleep (1) ;}} return NULL ;}int main (int argc, char ** argv) {pthread_t t1, t2, t3; pthread_rwlock_init (& g_rwlock, NULL); pthread_create (& t1, NULL, func, (void *) READ_THREAD); pthread_create (& t2, NULL, func, (void *) READ_THREAD ); pthread_create (& t3, NULL, func, (void *) WRITE_THREAD); pthread_join (t1, NULL); pthread_join (t2, NULL); pthread_join (t3, NULL ); pthread_rwlock_destroy (& g_rwlock); return 0 ;}

 

Condition variable

Condition variables are a synchronization mechanism available for threads. Condition variables provide multiple threads with a single round. Condition variables are used together with mutex, A thread is allowed to wait for a specific condition to occur in a non-competitive manner. Conditional variables are protected by mutex. The thread must first lock the mutex before changing the conditional State. Other threads will not detect this change before obtaining the mutex, because the mutex must be locked before changing the condition.

# Include <pthread. h> pthread_cond_init (pthread_cond_t * restrict cond, const limit * restrict attr); pthread_cond_destroy (pthread_cont_t * cond); // success returns 0, otherwise the error code is returned.

Call pthread_cond_init before using the conditional variable for initialization. after use, call pthread_cond_destroy for cleanup. Unless you need to create a condition variable with non-default attributes, the attr parameter of the pthread_cond_init function can be set to NULL.

# Include <pthread. h> int pthread_cond_broadcast (pthread_cond_t * cond); int aggregate (pthread_cond_t * cond, aggregate * mutex); // success returns 0; otherwise, the error code is returned.

The mutex passed to pthread_cond_wait protects the condition. The caller passes the lock mutex to the function, and the function then automatically puts the call thread on the thread list of the waiting condition to unlock the mutex. This closes the time channel between the condition check and the waiting condition for the thread to enter the sleep state to change the two operations, so that the thread will not miss any change in the condition. When the pthread_cond_wait function returns, the mutex is locked again.

Pthread_cond_broadcast wakes up all threads waiting for condition variables in the form of broadcast. Pthread_cond_signal is used to wake up a thread with a waiting condition variable. The reason for which the thread is awakened depends on the thread priority and scheduling mechanism. Sometimes you need to wake up a specified thread, but pthread does not provide a solution to this problem. This requirement can be achieved indirectly: define a global variable that uniquely represents the target thread. Set this variable as the target thread before waking up the thread of the waiting condition variable, then, it wakes up all the threads waiting for the condition variable in broadcast form. After these threads are awakened, they all check whether the change volume is themselves. If so, they start to execute the subsequent Code; otherwise, they will continue to wait.

Conditional Variable program example:

# Include <stdio. h> # include <stdlib. h> # include <pthread. h> # define err_sys (msg) \ do {perror (msg); exit (-1);} while (0) # define err_exit (msg) \ do {fprintf (stderr, msg); exit (-1);} while (0) pthread_cond_t cond; void * r1 (void * arg) {pthread_mutex_t * mutex = (pthread_mutex_t *) arg; static int cnt = 10; while (cnt --) {printf ("r1: I am wait. \ n "); pthread_mutex_lock (mutex); pthread_cond_wait (& cond, mutex);/* The mutex parameter is used to protect the mutex lock of condition variables, before calling pthread_cond_wait, mutex must lock */unlock (mutex);} return "r1 over";} void * r2 (void * arg) {pthread_mutex_t * mutex = (pthread_mutex_t *) arg; static int cnt = 10; while (cnt --) {// pthread_mutex_lock (mutex); // printf ("r2: I am send the cond signal. \ n "); pthread_cond_signal (& cond); // pthread_mutex_unlock (mutex); sleep (1);} return" r2 over ";} int main (void) {pthread_mutex_t mutex; pthread_t t1, t2; char * p1 = NULL; char * p2 = NULL; pthread_mutex_init (& mutex, NULL); pthread_cond_init (& cond, NULL); pthread_create (& t1, NULL, r1, & mutex); pthread_create (& t2, NULL, r2, & mutex); pthread_join (t1, (void **) & p1); pthread_join (t2, (void **) & p2); pthread_cond_destroy (& cond); pthread_mutex_destroy (& mutex); printf ("s1: % s \ n", p1); printf ("s2: % s \ n ", p2); return 0 ;}

 

Spin lock

The spin lock is similar to the mutex, but it does not block the process through sleep, but is always in the busy (spin) state before obtaining the lock. The spin lock can be used in the following situations: the lock is held for a short period of time, and the thread does not want to re-schedule it too much. Spin locks are usually used as underlying primitives to implement other types of locks. Based on the system architecture they are based on, they can be effectively implemented by using tests and setting instructions. Of course, the effectiveness mentioned here will also lead to a waste of CPU resources: When the thread spin lock becomes available, the CPU cannot do anything else, this is why the spin lock can only be used for a short period of time.

#include <pthread.h>int pthread_spin_init(pthread_spinlock_t *lock, int pshared);int pthread_spin_destroy(pthread_spinlock_t *lock);

The pshared parameter indicates the process sharing attribute, indicating how the spin lock is obtained. If it is set to PTHREAD_PROCESS_SHARED, the spin lock can be obtained by threads that can access the underlying memory of the lock, even those threads belong to different processes. Otherwise, if the pshared parameter is set to PTHREAD_PROCESS_PROVATE, the spin lock can only be accessed by internal threads of the Process initiating the lock.

#include <pthread.h>int pthread_spin_lock(pthread_spinlock_t *lock);int pthread_spin_trylock(pthread_spinlock_t *lock);int pthread_spin_unlock(pthread_spinlock_t *lock);

If the spin lock is currently unlocked, The pthread_spin_lock function can lock it instead of spinning. It attempts to unlock the spin lock without a lock and the result is undefined. Note: Do not use functions that may enter sleep state when they hold a spin lock. If these functions are called, CPU resources will be wasted, it takes longer for other threads to obtain the spin lock.

Example of spin lock usage:

#include <stdio.h>#include <stdlib.h>#include <pthread.h>pthread_spinlock_t g_lock;int g_data = 0;void *func(void *arg){    while (1) {        pthread_spin_lock(&g_lock);        g_data++;        printf("----------- %d\n", g_data);        sleep(1);        pthread_spin_unlock(&g_lock);    }}int main(int argc, char **argv){    pthread_t tid;    pthread_spin_init(&g_lock, PTHREAD_PROCESS_PRIVATE);    pthread_create(&tid, NULL, func, NULL);    pthread_create(&tid, NULL, func, NULL);    pthread_create(&tid, NULL, func, NULL);    pthread_join(tid, NULL);    return 0;}

 

Barrier

A barrier is a synchronization mechanism that allows users to coordinate multiple threads to work in parallel. The barrier allows each thread to wait until all the threads that work together reach a certain point and then continue execution from this point. Pthread_join is actually a barrier that allows one thread to wait until another thread exits. However, the barrier objects have a wider concept. They allow any number of threads to wait until all threads finish the processing, and threads do not need to exit. After all threads reach the barrier, they can continue to work.

# Include <pthread. h> int pthread_barrier_init (pthread_barrier_t * restrict barrier, const partition * restrict attr, unsigned int count); int partition (pthread_barrier_t * barrier); // return 0 if the call succeeds.

During barrier initialization, you can use the count parameter to specify the number of barrier threads that must be reached before all threads can continue to run. Attr specifies the barrier attribute. NULL is the default attribute.

# Include <pthread. h> int pthread_barrier_wait (pthread_barrier_t * barrier); // 0 is returned for success; otherwise, an error number is returned.

You can use the pthread_barrier_wait function to indicate that the thread has finished its work and the preparation is waiting for all other threads to come over. The thread that calls pthread_barrier_wait enters sleep state when the barrier count does not meet the conditions. If this thread is the last thread that calls pthread_barrier_wait, all threads will be awakened.

Once the barrier Count value is reached and the thread is not blocked, the barrier can be reused.

Barrier use example:

#include <stdio.h>#include <stdlib.h>#include <pthread.h>pthread_barrier_t g_barrier;void *func(void *arg){    int id = (int )arg;    if (id == 0) {        printf("thread 0\n");        sleep(1);        pthread_barrier_wait(&g_barrier);        printf("thread 0 come...\n");    }    else if (id == 1) {        printf("thread 1\n");        sleep(2);        pthread_barrier_wait(&g_barrier);        printf("thread 1 come...\n");        }    else if (id == 2) {        printf("thread 2\n");        sleep(3);        pthread_barrier_wait(&g_barrier);        printf("thread 2 come...\n");    }    return NULL;}int main(int argc, char **argv){    pthread_t t1, t2, t3;    pthread_barrier_init(&g_barrier, NULL, 3);    pthread_create(&t1, NULL, func, (void *)0);    pthread_create(&t2, NULL, func, (void *)1);    pthread_create(&t3, NULL, func, (void *)2);    pthread_join(t1, NULL);    pthread_join(t2, NULL);    pthread_join(t3, NULL);    return 0;}

 

Refer:

1. Thread chapter in the third edition of advanced programming in UNIX environment

2. ThinkInTechnology

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.