[Linux C multi-thread programming] mutex lock and condition variable

Source: Internet
Author: User

I. mutex lock

In essence, mutex is a lock that provides protection for access to shared resources.

1. Initialization:

In Linux, the thread mutex data type is pthread_mutex_t. Before use, initialize it:

For the mutex of static allocation, you can set it to pthread_mutex_initializer or call pthread_mutex_init.

For the dynamically allocated mutex, after applying for memory (malloc), initialize with pthread_mutex_init and call pthread_mutex_destroy before releasing the memory (free.

Prototype:

Int pthread_mutex_init (pthread_mutex_t * restrict mutex, const pthread_mutexattr_t * restric ATTR );

Int pthread_mutex_destroy (pthread_mutex_t * mutex );

Header file:

Return Value: 0 is returned for success, and error number is returned for error.

Note: If you use the default attribute to initialize mutex, you only need to set ATTR to null. Other values will be explained later.

2. mutex operation:

To access shared resources, You need to lock the mutex. If the mutex has been locked, the calling thread will be blocked until the mutex is unlocked. after accessing shared resources, unlock the mutex.

First, let's talk about the locking function:

Header file:

Prototype:

Int pthread_mutex_lock (pthread_mutex_t * mutex );

Int pthread_mutex_trylock (pthread_mutex_t * mutex );

Return Value: 0 is returned for success, and error number is returned for error.

Description: The trylock function is a non-blocking call mode. That is, if the mutex is not locked, the trylock function locks the mutex, and get access to the shared resources. If the mutex is locked, the trylock function will not block the wait and return ebusy directly, indicating that the shared resources are busy.

Let's talk about the function:

Header file:

Prototype: int pthread_mutex_unlock (pthread_mutex_t * mutex );

Return Value: 0 is returned for success, and error number is returned for error.

3. deadlock:

A deadlock occurs when multiple dependency locks exist, and occurs when a thread tries to lock the mutex in the reverse order of the other thread. It is important to avoid a deadlock when mutex is used.

In general, there are several unwritten basic principles:

You must obtain the lock before performing operations on shared resources.

The lock must be released after the operation is completed.

Use the lock as soon as possible.

If there are multiple locks, for example, if the order of acquisition is ABC, the release order should also be ABC.

When a thread returns an error, it should release the lock it obtained.

Next we will give a test applet to learn more about mutex. mutex semaphores lock a program instead of a variable. For a mutex variable testlock, after executing the first pthread_mutex_lock (testlock) and before the unlock (testlock), if other threads also execute pthread_mutex_lock (testlock ), this thread will be blocked until the previous thread is unlocked and can be executed. Thus, synchronization can protect critical zone resources.
# Include <stdio. h>
# Include <pthread. h>

Static pthread_mutex_t testlock;
Pthread_t test_thread;

Void * test ()
{
Pthread_mutex_lock (& testlock );
Printf ("thread test () \ n ");
Pthread_mutex_unlock (& testlock );
}

Int main ()
{
Pthread_mutex_init (& testlock, null );
Pthread_mutex_lock (& testlock );

Printf ("Main lock \ n ");

Pthread_create (& test_thread, null, test, null );
Sleep (1); // more clearly observe whether the mutex lock for the Creation thread has been executed
Printf ("Main unlock \ n ");
Pthread_mutex_unlock (& testlock );

Sleep (1 );

Pthread_join (test_thread, null );
Pthread_mutex_destroy (& testlock );
Return 0;
}

Make
Gcc-d_reentrant-lpthread-O test. c

Result:
Main lock
Main unlock
Thread test ()

Ii. Condition Variables
The usage of pthread_cond_wait () is described below.
A condition variable is a mechanism for synchronizing global variables shared by threads. It mainly includes two actions: one thread waits for the condition variable to be established and suspends; another thread sets "condition to true" (a signal indicating condition to true ). To prevent competition, the use of condition variables is always combined with a mutex lock.

1. Create and log out

Like mutex lock, conditional variables have two static and dynamic creation methods. The static method uses the pthread_cond_initializer constant, as shown below:
Pthread_cond_t cond = pthread_cond_initializer

Call the pthread_cond_init () function dynamically. The API is defined as follows:
Int pthread_cond_init (pthread_cond_t * cond, pthread_condattr_t * cond_attr)

Although the POSIX standard defines attributes for condition variables, they are not implemented in linuxthreads. Therefore, the cond_attr value is usually null and ignored.

To cancel a condition variable, you must call pthread_cond_destroy (). This condition variable can be canceled only when no thread is waiting for the condition variable. Otherwise, ebusy is returned. Because the condition variables implemented in Linux do not allocate any resources, the logout action only includes checking whether there are waiting threads. The API is defined as follows:
Int pthread_cond_destroy (pthread_cond_t * Cond)

2. Waiting and stimulating

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)

There are two waiting conditions: pthread_cond_wait () and pthread_cond_timedwait (). If the waiting time is not met before the given time, etimeout is returned and the waiting time ends, here, abstime appears in the absolute time format of the same meaning as the time () system call. 0 indicates GMT 00:00:00, January 1, January 1, 1970.

Either way, you must work with a mutex lock to prevent multiple threads from simultaneously requesting the Race Condition of pthread_cond_wait () (or pthread_cond_timedwait (), the same below ). Mutex locks must be common locks (pthread_mutex_timed_np) or adaptive locks (pthread_mutex_adaptive_np ),Before calling pthread_cond_wait (), the current thread must lock (pthread_mutex_lock (). Before the Update Conditions wait for the queue, mutex remains locked and is unlocked before the thread is suspended and waiting. Before the condition is met and pthread_cond_wait () is left, mutex is re-locked to match the lock action before pthread_cond_wait.When pthread_cond_wait () is executedAutomatically unlock mutex (like executing pthread_unlock_mutex) and wait for the condition variable to trigger. At this time, the thread hangs and does not occupy the CPU time until the condition variable is triggered.

Therefore, the entire process can be described as follows:

(1) pthread_mutex_lock () lock,

(2) pthread_cond_wait () Wait. The wait process is divided into unlock, condition fulfillment, and lock.

(3) Unlock pthread_mutex_unlock.
There are two ways to activate the condition. pthread_cond_signal () activates a thread waiting for the condition. When there are multiple waiting threads, one of them is activated in the queue order; and pthread_cond_broadcast () activate all the waiting threads. If the two do not have a waiting thread, nothing will be done.

The following question explains the above description:

When pthread_cond_t calls pthread_cond_wait to enter the waiting state, the pthread_mutex_t mutex signal is invalid.

The sample code is as follows:

// Multithread synchronization-conditional lock (equivalent to Windows events) test

// It is valid only when pthread_cond_wait enters the waiting signal state before pthread_cond_signal can be called.

// Pthread_cond_signal cannot be executed before pthread_cond_wait

# Include <stdio. h>

# Include <pthread. h> // header file for Multithreading

# Include <semaphore. h> // use the header file for semaphores

Pthread_cond_t g_cond/* = pthread_mutex_initializer */; // declare the lock and initialize it with a macro.

Pthread_mutex_t g_mutex;

// The thread executes the function.

Void threadfun1 (void)

{

Int I;

Pthread_mutex_lock (& g_mutex); // 1

Pthread_cond_wait (& g_cond, & g_mutex); // congestion if g_cond has no signal

For (I = 0; I <2; I ++ ){

Printf ("thread threadfun1. \ n ");

Sleep (1 );

}

Pthread_cond_signal (& g_cond );

Pthread_mutex_unlock (& g_mutex );

}

Int main (void)

{

Pthread_t id1; // the ID of the thread

Pthread_t Id2;

Pthread_cond_init (& g_cond, null); // It can also be initialized in the program

Pthread_mutex_init (& g_mutex, null); // mutex variable Initialization

Int I, RET;

Ret = pthread_create (& id1, null, (void *) threadfun1, null );

If (Ret! = 0) {// if the value is not 0, the thread creation fails.

Printf ("create pthread1 error! \ N ");

Exit (1 );

}

Sleep (5); // wait for the sub-thread to start first

Pthread_mutex_lock (& g_mutex); // 2

Pthread_cond_signal (& g_cond); // give a start signal. Note that you must wait for the sub-thread to enter the waiting state before sending a signal. Otherwise, the signal is invalid.

Pthread_mutex_unlock (& g_mutex );

Pthread_join (id1, null );

Pthread_cond_destroy (& g_cond); // release

Pthread_mutex_destroy (& g_mutex); // release

Return 0;

}

Please refer to the red colors 1 and 2.

It is clear that 1 locks the mutex variable first, but the code can still be locked when it is executed to 2.

Why ???? /

Pthread_cond_wait () will be locked and continue running... now let's look at a typical application: just look at the annotations.

Explanation: when the program enters pthread_cond_wait, it will unlock g_mutex and re-lock g_mutex before leaving pthread_cond_wait. So g_mutex in main will be locked. Haha...

Now let's look at a typical application: just look at the annotations.

# Include <pthread. h>
# Include <unistd. h>

Static pthread_mutex_t CTX = pthread_mutex_initializer;
Static pthread_cond_t cond = pthread_cond_initializer;

Struct node {
Int n_number;
Struct node * n_next;
} * Head = NULL;

/* [Thread_func] */
Static void cleanup_handler (void * Arg)
{
Printf ("cleanup handler of second thread. \ n ");
Free (ARG );
(Void) pthread_mutex_unlock (& CTX );
}

Static void * thread_func (void * Arg)
{
Struct node * P = NULL;

Pthread_cleanup_push (cleanup_handler, P );
While (1 ){
Pthread_mutex_lock (& CTX); // this mutex is mainly used to ensure the concurrency of pthread_cond_wait.
While (Head = NULL) {// This while should be particularly noted that a single pthread_cond_wait function is very complete. Why is there a while (Head = NULL) here? Because the threads in pthread_cond_wait may be accidentally awakened, if this time head! = NULL is not what we want. At this time, the thread should continue to enter pthread_cond_wait
Pthread_cond_wait (& cond, & CTX); // pthread_cond_wait will first remove the previously-locked pthread_mutex_lock, and then block waiting for sleep in the column, wait until it is awakened again (in most cases, the waiting condition is set up, and then the process will first lock the first pthread_mutex_lock (& CTX); and then read the resource, this process is clear:/* block --> unlock --> wait () return --> lock */

# Include <unistd. h>
# Include <stdio. h>
# Include <stdlib. h>
# Include <sys/errno. h>
# Include <sys/types. h>
# Include <signal. h>
# Include <pthread. h>

# Define min (A, B) (a) <(B )? (A): (B ))
# Define max (A, B) (a)> (B )? (A): (B ))

# Define maxnitems 1000000
# Define maxnthreads 100

Int nitems;/* read-only by producer and consumer */
Struct {
Pthread_mutex_t mutex;
Int buff [maxnitems];
Int nput;
Int nval;
} Shared = {pthread_mutex_initializer };

Void * produce (void *), * consume (void *);

/* Include main */
Int
Main (INT argc, char ** argv)
{
Int I, nthreads, Count [maxnthreads];
Pthread_t tid_produce [maxnthreads], tid_consume;

If (argc! = 3 ){
Printf ("Usage: prodcons3 <# items> <# threads> \ n ");
Return-1;
}

Nitems = min (atoi (argv [1]), maxnitems );
Nthreads = min (atoi (argv [2]), maxnthreads );

/* 4 create all producers and one consumer */

For (I = 0; I <nthreads; I ++ ){
Count [I] = 0;
Pthread_create (& tid_produce [I], null, produce, & COUNT [I]);
}
Pthread_create (& tid_consume, null, consume, null );

/* 4 wait for all producers and the consumer */
For (I = 0; I <nthreads; I ++ ){
Pthread_join (tid_produce [I], null );
Printf ("count [% d] = % d \ n", I, Count [I]);
}
Pthread_join (tid_consume, null );

Exit (0 );
}
/* End main */

Void *
Produce (void * Arg)
{
For (;;){
Pthread_mutex_lock (& shared. mutex );
If (shared. nput> = nitems ){
Pthread_mutex_unlock (& shared. mutex );
Return (null);/* array is full, we're re done */
}
Shared. Buff [shared. nput] = shared. nval;
Shared. nput ++;
Shared. nval ++;
Pthread_mutex_unlock (& shared. mutex );
* (Int *) Arg) + = 1;
}
}

/* Include consume */
Void
Consume_wait (int I)
{
For (;;){
Pthread_mutex_lock (& shared. mutex );
If (I <shared. nput ){
Pthread_mutex_unlock (& shared. mutex );
Return;/* an item is ready */
}
Pthread_mutex_unlock (& shared. mutex );
}
}

Void *
Consume (void * Arg)
{
Int I;

For (I = 0; I <nitems; I ++ ){
Consume_wait (I );
If (shared. Buff [I]! = I)
Printf ("Buff [% d] = % d \ n", I, shared. Buff [I]);
}
Return (null );
}

Related Article

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.