Linux Multithreading (three) (synchronous mutex)

Source: Internet
Author: User
Tags posix ticket

1. Synchronization of threads with mutex 1.1. Mutex for threads

A set of mutex functions specifically for thread mutexes is defined in the POSIX thread. A mutex is a simple locking method to control access to a shared resource, which has only two states (lock and unlock) and can treat the mutex as a global variable in a sense. Why locks are required because multiple threads share the resources of a process, and when a common interval is accessed (global variable), when a thread accesses it, it needs to be locked to prevent another thread from accessing it, enabling exclusive access to the resource. Only one thread can master a mutex at a time, and a locked thread can manipulate the shared resource. If another thread wants to lock a locked mutex, the thread hangs until the locked thread releases the mutex.

1. Create and Destroy Locks

There are two ways to create mutexes, both statically and dynamically.

• Static mode:

POSIX defines a macro pthread_mutex_initializer to statically initialize the mutex, as follows:

pthread_mutex_t mutex = Pthread_mutex_initializer;

In the Linux threads implementation, pthread_mutex_t is a structure, and pthread_mutex_initializer is a macro constant.

• Dynamic mode:

The dynamic approach is to use the Pthread_mutex_init () function to initialize the mutex, and the API is defined as follows:

#include <pthread.h>

int Pthread_mutex_init (pthread_mutex_t *mutex, const pthread_mutexattr_t *mutexattr)

Where mutexattr is used to specify the mutex attribute (see below), and if NULL, the default property is used. is usually null

Pthread_mutex_destroy () is used to unregister a mutex, and the API is defined as follows:

int Pthread_mutex_destroy (pthread_mutex_t *mutex);

Destroying a mutex means freeing up the resources it occupies and requires that the lock is currently open. Because the mutex does not occupy any resources in Linux, Pthread_mutex_destroy () in Linux threads has no other action in addition to checking the lock state (the locked state returns EBUSY).

2. Mutex properties

The Mutex property structure is defined as:

typedef struct

{

int __mutexkind; Note that this is a two underline.

} pthread_mutexattr_t;

The properties of the mutex are specified when the lock is created, and in the linuxthreads implementation there is only one lock type attribute __mutexkind, and the different lock types behave differently when attempting to lock an already locked mutex, that is, whether to block the wait. There are three values to choose from:

· PTHREAD_MUTEX_TIMED_NP, this is the default value (direct write null is the default value), that is, the normal lock (or fast lock). When a line is Cheng, the remaining thread that requests the lock forms a blocking wait queue, and the lock is acquired by priority after unlocking. This locking strategy guarantees the fairness of resource allocation.

Example: Initialize a quick lock.

pthread_mutex_t lock;

Pthread_mutex_init (&lock, NULL);

· pthread_mutex_recursive_np, nested locks allow the same thread to be successfully acquired multiple times for the same lock, and unlocked by multiple unlock. If it is a different thread request, re-competes when the lock line threads unlocked.

Example: Initialize a nested lock.

pthread_mutex_t lock;

pthread_mutexattr_t mutexattr;

Mutexattr.__mutexkind = PTHREAD_MUTEX_RECURSIVE_NP;

Pthread_mutex_init (&lock, &mutexattr);

· PTHREAD_MUTEX_ERRORCHECK_NP, checks the wrong lock, returns EDEADLK if the same thread requests the same lock, otherwise the same as the PTHREAD_MUTEX_TIMED_NP type action. This ensures that the simplest case of deadlocks does not occur when multiple locks are not allowed. If the type of lock is a quick lock, and a line is Cheng and then locked, then it is the deadlock.

Example: Initialize a nested lock.

pthread_mutex_t lock;

pthread_mutexattr_t mutexattr;

Mutexattr.__mutexkind = PTHREAD_MUTEX_ERRORCHECK_NP;

Pthread_mutex_init (&lock, &mutexattr);

3. Lock operation

The lock operation mainly includes

locking int Pthread_mutex_lock (pthread_mutex_t *mutex)

unlock int pthread_mutex_unlock (pthread_mutex_t *mutex)

Test lock int Pthread_mutex_trylock (pthread_mutex_t *mutex)

Pthread_mutex_lock: Lock, no matter what kind of lock, can not be two different threads at the same time, and must wait to unlock. For a normal lock type, the unlocking can be any thread within the process, and the lock must be unlocked by the lock to be valid, otherwise the eperm is returned; for nested locks, the document and implementation requirements must be unlocked by the lock-in, but the experimental results show that there is no such limitation, and this difference is not yet explained. In a thread in the same process, if the lock is not unlocked, no other thread can acquire the lock again.

Pthread_mutex_unlock: Depending on the type of lock, different behaviors are implemented:

For the quick lock, the pthread_mutex_unlock is unlocked;

For a recursive lock, the Pthread_mutex_unlock makes the reference count of the lock minus 1;

For a check-in lock, if the lock is locked by the current thread, the lock is unlocked, otherwise nothing is done.

Pthread_mutex_trylock: Semantics are similar to Pthread_mutex_lock (), but the difference is that when a lock is already occupied, it returns EBUSY instead of suspending the wait.

Example: Comparing Pthread_mutex_trylock () with Pthread_mutex_lock ()

#include <stdio.h>

#include <pthread.h>

pthread_mutex_t lock;

void* pthfunc (void *args)

{

Pthread_mutex_lock (&lock); Add a lock first

Pthread_mutex_lock (&lock); Lock again, hang up blocking

Pthread_mutex_trylock (&lock); Lock with Trylock, it will not suspend blocking

printf ("hello\n");

Sleep (1);

Pthread_exit (NULL);

}

Main ()

{

pthread_t Pthid = 0;

Pthread_mutex_init (&lock,null);

Pthread_create (&pthid,null,pthfunc,null);

Pthread_join (Pthid,null);

Pthread_mutex_destroy (&lock);

}

4. Locking precautions

If a thread is canceled before it is unlocked, the lock will remain locked forever, so if a cancellation point exists within the critical section, it must be unlocked in the exit callback function Pthread_cleanup_push/pthread_cleanup_pop. The mutex should not be used in the signal processing function, otherwise it is easy to cause deadlock.

5. Mutex instance

Example: Train station ticket (if not locked here, there will be a case of selling negative votes)

#include <stdio.h>

#include <pthread.h>

int ticketcount = 20; Train tickets, public resources (global)

void* salewinds1 (void* args)//ticket Gate 1

{

while (Ticketcount > 0)//If there is a ticket, the ticket is sold

{

printf ("windows1 Start sale ticket!the ticket is:%d\n", Ticketcount);

Sleep (3); Selling a ticket takes 3 seconds to operate.

Ticketcount--; Ticket out

printf ("Sale ticket finish!,the Last ticket is:%d\n", Ticketcount);

}

}

void* salewinds2 (void* args)//ticket Gate 2

{

while (Ticketcount > 0)//If there is a ticket, the ticket is sold

{

printf ("Windows2 Start sale ticket!the ticket is:%d\n", Ticketcount);

Sleep (3); Selling a ticket takes 3 seconds to operate.

Ticketcount--; Ticket out

printf ("Sale ticket finish!,the Last ticket is:%d\n", Ticketcount);

}

}

int main ()

{

pthread_t pthid1 = 0;

pthread_t Pthid2 = 0;

Pthread_create (&pthid1,null,salewinds1,null); Thread 1

Pthread_create (&pthid2,null,salewinds2,null); Thread 2

Pthread_join (Pthid1,null);

Pthread_join (Pthid2,null);

return 0;

}

Example: Train ticket after locking

#include <stdio.h>

#include <pthread.h>

int ticketcount = 20;

pthread_mutex_t lock;

void* salewinds1 (void* args)

{

while (1)

{

Pthread_mutex_lock (&lock); Because you want to access the global shared variable, you need to lock

if (Ticketcount > 0)//If there is a ticket

{

printf ("windows1 Start sale ticket!the ticket is:%d\n", Ticketcount);

Sleep (3); Selling a ticket takes 3 seconds to operate.

Ticketcount--; Ticket out

printf ("Sale ticket finish!,the Last ticket is:%d\n", Ticketcount);

}

else//If there is no ticket

{

Pthread_mutex_unlock (&lock); Unlock

Pthread_exit (NULL); Exit thread

}

Pthread_mutex_unlock (&lock); Unlock

Sleep (1); Put it outside the lock, let the other have time to lock.

}

}

void* salewinds2 (void* args)

{

while (1)

{

Pthread_mutex_lock (&lock); Because you want to access the global shared variable, you need to lock

if (ticketcount>0)//If there is a ticket

{

printf ("Windows2 Start sale ticket!the ticket is:%d\n", Ticketcount);

Sleep (3); Selling a ticket takes 3 seconds to operate.

Ticketcount--; Ticket out

printf ("Sale ticket finish!,the Last ticket is:%d\n", Ticketcount);

}

else//If there is no ticket

{

Pthread_mutex_unlock (&lock); Unlock

Pthread_exit (NULL); Exit thread

}

Pthread_mutex_unlock (&lock); Unlock

Sleep (1); Put it outside the lock, let the other have time to lock.

}

}

int main ()

{

pthread_t pthid1 = 0;

pthread_t Pthid2 = 0;

Pthread_mutex_init (&lock,null); Initialize lock

Pthread_create (&pthid1,null,salewinds1,null); Thread 1

Pthread_create (&pthid2,null,salewinds2,null); Thread 2

Pthread_join (Pthid1,null);

Pthread_join (Pthid2,null);

Pthread_mutex_destroy (&lock); Destroying locks

return 0;

}

Summary: Thread mutex mutex: The lock step is as follows:

1. Define a global pthread_mutex_t lock; or with

pthread_mutex_t lock = Pthread_mutex_initializer; Init is not used in the main function

2. In main, call the Pthread_mutex_init function to initialize

3. Call Pthread_mutex_lock lock in the child thread function

4. Call Pthread_mutex_unlock in the child thread function to unlock

5. Finally call the Pthread_mutex_destroy function in main to destroy

1.2. Synchronization of threads 6 condition variables

A conditional variable is a mechanism for synchronizing a global variable shared between threads, consisting of two actions: one thread waits for a condition variable to be set up, and the other thread makes the condition set (giving the conditional signal). To prevent competition, the use of condition variables is always combined with a mutex.

1. Creating and Unregistering

Conditional variables, like mutexes, are created in both static and dynamic ways:

Static mode makes the Pthread_cond_initializer constant, as follows:

pthread_cond_t cond = Pthread_cond_initializer;

The Pthread_cond_init () function is called dynamically, and 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 a conditional variable, it is not implemented in Linux threads, so the cond_attr value is usually null and ignored.

Unregistering a condition variable calls Pthread_cond_destroy () and returns EBUSY if no thread is waiting on the condition variable to unregister the condition variable. Because the condition variables implemented by Linux do not have any resources assigned to them, the logoff action includes checking for waiting threads only. The API is defined as follows:

int Pthread_cond_destroy (pthread_cond_t *cond);

2. Wait and inspire

Wait conditions are available in two ways: unconditionally Waiting for pthread_cond_wait () and timing waiting for pthread_cond_timedwait ():

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);

The thread unlocks the lock that the mutex points to and is blocked by the condition variable cond. The time-to-wait means that after the abstime period, even if the condition variable is not satisfied, the blocking is lifted. Regardless of the wait mode, you must mate with a mutex to prevent multiple threads from simultaneously requesting a race condition (Race Condition) for pthread_cond_wait () (or pthread_cond_timedwait (), hereinafter). The mutex mutex must be a normal lock (PTHREAD_MUTEX_TIMED_NP) and must be Cheng (Pthread_mutex_lock ()) by this line before calling Pthread_cond_wait (), and before the update condition waits for the queue, The mutex remains locked and unlocked before the thread hangs into the wait. Before the condition satisfies thereby leaving pthread_cond_wait (), the mutex will be re-locked to correspond to the lock action before entering Pthread_cond_wait (). (That is, before you do pthread_cond_wait, you tend to lock with Pthread_mutex_lock, and the call pthread_cond_wait function unlocks the lock and then suspends the thread from blocking.) Until the condition is pthread_cond_signal fired, the lock state is restored to a locked state, and then unlocked with Pthread_mutex_unlock).

There are two forms of the excitation condition, pthread_cond_signal () activates a thread waiting for the condition, one is activated in the queued order when there are multiple waiting threads, and pthread_cond_broadcast () activates all waiting threads

3. Other

Both pthread_cond_wait () and pthread_cond_timedwait () are implemented as cancellation points, which means that if pthread_cond_wait () is canceled, the block is exited, and the lock state is restored. At this point the mutex is locked, and the current thread has been canceled, then the unlocked operation will not be executed, when the lock is not released, it will cause a deadlock, so you need to define the exit callback function to unlock it.

The following example focuses on the use of mutexes and conditional variables, and the effect of cancellation on conditional wait actions. In the example, two threads are started and wait for the same condition variable, and if you do not use the exit callback function (see the comment section in the example), Tid2 will wait forever at Pthread_mutex_lock (). If you use a callback function, the conditional wait on the TID2 and the conditional excitation of the main thread will work correctly.

Instance:

#include <stdio.h>

#include <pthread.h>

#include <unistd.h>

pthread_mutex_t Mutex;

pthread_cond_t cond;

void Threadclean (void *arg)

{

Pthread_mutex_unlock (&mutex);

}

void * Child1 (void *arg)

{

Pthread_cleanup_push (Threadclean,null); 1

while (1) {

printf ("Thread 1 get running \ n");

printf ("Thread 1 Pthread_mutex_lock returns%d\n", Pthread_mutex_lock (&mutex));

Pthread_cond_wait (&cond,&mutex); Wait for the parent process to send a signal

printf ("Thread 1 condition applied\n");

Pthread_mutex_unlock (&mutex);

Sleep (5);

}

Pthread_cleanup_pop (0); 2

return 0;

}

void *child2 (void *arg)

{

while (1) {

Sleep (3); 3

printf ("Thread 2 Get running.\n");

printf ("Thread 2 Pthread_mutex_lock returns%d\n", Pthread_mutex_lock (&mutex));

Pthread_cond_wait (&cond,&mutex);

printf ("Thread 2 condition applied\n");

Pthread_mutex_unlock (&mutex);

Sleep (1);

}

}

int main (void)

{

pthread_t Tid1,tid2;

printf ("Hello, condition variable test\n");

Pthread_mutex_init (&mutex,null);

Pthread_cond_init (&cond,null);

Pthread_create (&tid1,null,child1,null);

Pthread_create (&tid2,null,child2,null);

while (1) {//Parent thread

Sleep (2); 4

Pthread_cancel (TID1); 5

Sleep (2); 6

Pthread_cond_signal (&cond);

}

Sleep (10);

return 0;

}

Without commenting, the unlock in child1 is not executed, the lock has not been closed, and the lock in Child2 cannot execute lock, it will always wait at Pthread_mutex_lock () forever. If you do not do the Pthread_cancel () Action of note 5, both child1 and child2 work correctly, even without the sleep () delay operation. The delay of note 3 and note 4 allows the child1 to complete the cancellation action, allowing Child2 to enter the request lock operation after the child1 exits. If there is no callback function definition for comments 1 and 2, the system hangs where the Child2 requests the lock, because Child1 does not release the lock, and if the delay of the comment 3 and note 4 is not done at the same time, child2 can be controlled before Child1 completes the cancellation action, thus successfully executing the request for the lock. , but it may hang in pthread_cond_wait () because there is also an operation to apply for a mutex. The Child1 function gives the use of the standard conditional variable: The callback function is protected, waits for the condition to be locked, and pthread_cond_wait () is returned to unlock.

The conditional variable mechanism, like a mutex, cannot be used in signal processing, and calling pthread_cond_signal () or Pthread_cond_broadcast () in a signal processing function is likely to cause a deadlock.

Example: Train ticket, using condition variables, when the train ticket sold out, and then re-set the number of votes to 10;

#include <pthread.h>

#include <stdio.h>

int ticketcount = 10;

pthread_mutex_t lock; Mutual exclusion Lock

pthread_cond_t cond; Condition variable

void* salewinds1 (void* args)

{

while (1)

{

Pthread_mutex_lock (&lock); Because you want to access the global shared variable ticketcount, you need to lock

if (Ticketcount > 0)//If there is a ticket

{

printf ("windows1 Start sale ticket!the ticket is:%d\n", Ticketcount);

Ticketcount--;//sells a ticket.

if (Ticketcount = = 0)

Pthread_cond_signal (&cond); Notice there's no tickets.

printf ("Sale ticket finish!,the Last ticket is:%d\n", Ticketcount);

}

else//If there is no ticket, unlock the exit

{

Pthread_mutex_unlock (&lock);

Break

}

Pthread_mutex_unlock (&lock);

Sleep (1); To put it outside the lock.

}

}

void* salewinds2 (void* args)

{

while (1)

{

Pthread_mutex_lock (&lock);

if (Ticketcount > 0)

{

printf ("Windows2 Start sale ticket!the ticket is:%d\n", Ticketcount);

Ticketcount--;

if (Ticketcount = = 0)

Pthread_cond_signal (&cond); Send Signal

printf ("Sale ticket finish!,the Last ticket is:%d\n", Ticketcount);

}

Else

{

Pthread_mutex_unlock (&lock);

Break

}

Pthread_mutex_unlock (&lock);

Sleep (1);

}

}

void *setticket (void *args)//re-set the number of votes

{

Pthread_mutex_lock (&lock); Because you want to access the global variable ticketcount, you need to lock

if (Ticketcount > 0)

Pthread_cond_wait (&cond,&lock); If you have a ticket, unlock it and block it until you do not have a ticket.

Ticketcount = 10; Re-set the number of votes to 10

Pthread_mutex_unlock (&lock); Unlock

Sleep (1);

Pthread_exit (NULL);

}

Main ()

{

pthread_t pthid1,pthid2,pthid3;

Pthread_mutex_init (&lock,null); Initialize lock

Pthread_cond_init (&cond,null); Initializing condition variables

Pthread_create (&pthid1,null, salewinds1,null); Creating Threads

Pthread_create (&pthid2,null, salewinds2,null);

Pthread_create (&pthid3,null, setticket,null);

Pthread_join (Pthid1,null); Wait for the child thread to finish executing

Pthread_join (Pthid2,null);

Pthread_join (Pthid3,null);

Pthread_mutex_destroy (&lock); Destroying locks

Pthread_cond_destroy (&cond); Destroying condition variables

}

Linux Multithreading (three) (synchronous mutex)

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.