UNIX Network Programming-synchronization

Source: Internet
Author: User
Tags posix

1. Mutual exclusion Lock (volume) and condition variable

By default, mutexes and condition variables are used for inter-thread synchronization, and can be used for inter-process synchronization if they are placed in a shared memory area.

1.1 Mutual exclusion Lock

1. Overview:
Mutexes (mutexes, also known as mutexes), prevent multiple threads from doing read and write operations on a common resource to ensure the integrity of the shared data.

To protect critical sections to ensure that only one thread (or process) accesses shared resources (such as code snippets) at any time. The code form of the Protection critical section:

lock_the_mutex(...);临界区unlock_the_mutex(...);

Only one thread can lock a given mutex at any time.

The following three functions lock and unlock a mutex:

#include<pthread.h>int*mptr);int*mptr);int*mptr);

The above three functions, if the call succeeds, return 0, and the corresponding error value is returned.

If you try to give a mutex lock that has been locked by a thread, it pthread_mutex_lock will block until the mutex is unlocked. pthread_mutex_trylockis the corresponding non-blocking function, and if the mutex is locked, it returns a ebusy error.

If multiple threads are blocked waiting for the same mutex, which thread will begin to run when the mutex is unlocked: different threads are given different priorities, and the synchronization function wakes up the highest-priority blocked threads.

2, the mutex implementation of the producer-consumer model:

Producer-consumer issues are also known as bounded buffer issues, with several producers and several consumers sharing a fixed number of buffers, resulting in synchronization and communication problems .

The various IPC instruments themselves are examples of a producer-consumer problem.
synchronization of Pipelines, FIFO, and Message Queuing is implicit , and the user can only use these IPC methods through the specified interface, where synchronization is done by the kernel.

shared memory, as IPC, requires explicit synchronization by the user, and the sharing of global data between threads requires explicit synchronization.

Take a multi-producer, single-consumer model for example:

There are multiple producer threads and a single consumer thread in a single process, and we only care about the synchronization between multiple producer threads until all the producer threads have finished working before the consumer thread is started.

#include <stdio.h>#include <stdlib.h>#include <pthread.h>#include <unistd.h>#include <math.h>#define MAXNITEMS 1000000#define MaxnthreadsintNitems;//(1) Number of entries stored by producers, read-only (for producers or consumers)!!! the variables in the/**shared structure are shared data * /struct{//(2)!!! pthread_mutex_t Mutex;/* Sync variable: Mutex */    intBuff[maxnitems];//producers will store data for the buff array in turn    intNput;/*nput is the next element in the buff array.    intNval;/*nval is the next value to store (0,1,2, etc.) */} shared = {Pthread_mutex_initializer//(3) Initialize the mutex used for synchronization between producer Threads!!! };void*produce (void*), *consume (void*);intMainintargcChar*argv[]) {/* Variable description: tid_produce[] The thread ID of each thread in the array is saved *count[] is the ID of each thread counter *tid_consume that holds a single consumer * /    intI, Nthreads, count[maxnthreads]; pthread_t Tid_produce[maxnthreads], tid_consume;/ * Command line parameter count * /    if(ARGC! =3) {printf("Usage:producer_consumer1 < #iterms > < #threads >\n");Exit(1); }*/ * * Argv[1] Specify number of entries in the producer (* Argv[2] to be created * *Nitems = Min (atoi (argv[1]), maxnitems);//(4) Specify the number of entries the producer holds!!! nthreads = Min (atoi (argv[2]), maxnthreads);//(5) How many producer threads are created!!!     / * The *set_concurrency function is used to tell the thread system how many threads we want to run concurrently * to set the concurrency level * /Set_concurrency (nthreads);//(6)!!!     //(7) Create producer thread: Each thread executes produce!!!      for(i =0; i < nthreads; i++) {///buff[i] set to ICount[i] =0;The//counter is initialized to 0, and each thread adds 1 to this counter each time it stores an entry in the buffer.Pthread_create (&tid_produce[i], NULL, produce, &count[i]); }//(8) Wait for all producer threads to terminate and output counter values for each thread!!!      for(i =0; i < nthreads; i++) {pthread_join (tid_produce[i], NULL);printf("count[%d] =%d\n", I, count[i]); }//(9) then start a single consumer thread!!! Pthread_create (&tid_consume, NULL, consume, NULL);//(10) then wait for the consumer to complete and then terminate the process!!! Pthread_join (Tid_consume, NULL);return 0;}//Create producer Threadsvoid*produce (void*arg) { for( ; ; ) {Pthread_mutex_lock (&shared.mutex);//(1) lock!!!         //(2) critical section        if(Shared.nput >= Nitems) {//Description at this time the production is complete, unlockPthread_mutex_unlock (&shared.mutex);return(NULL);        } Shared.buff[shared.nput] = Shared.nval;        shared.nput++;        shared.nval++; Pthread_mutex_unlock (&shared.mutex);//(3) Unlock!!!         The addition of the//count element (via pointer Arg) does not belong to the critical section because each thread has its own counter*((int*) arg) + =1; }}//wait for the producer thread and then start the consumer threadvoid*consume (void*arg) {intI/ * * The consumer simply verifies that the entry in the buff is correct, and outputs a message if an error is found * This function is only one instance running, and after all the producer threads have completed * So no synchronization is required * *     for(i =0; i < Nitems; i++)if(Shared.buff[i]! = i)printf("buff[%d] =%d\n", I, shared.buff[i]);return(NULL);}

3. Non-normal termination of mutual exclusion Lock:

if the process terminates while holding the mutex, the kernel is not responsible for automatically releasing the held lock. The only synchronization lock type that the kernel automatically cleans is the FCNTL record lock.
If the holding process or thread of the locked mutex is terminated, the mutex cannot be unlocked and thus deadlock. Threads can install a thread cleaner that can be used to release held locks when they are canceled. However, this release may cause the shared object's state to be partially updated, resulting in inconsistencies.

1.2 Item variables

A mutex can only be used for locking , to implement mutually exclusive access to a shared object, and cannot be used to wait for an event. the condition variable is used for waiting.

#include<pthread.h>int*cond*mutex);intint*cond);

Each condition variable needs to be associated with a mutex to provide mutually exclusive access to the wait condition.

2. Read/write Lock

Read and write locks can be distinguished between reading data and modifying data. The rules are as follows:

1) When no thread holds a write lock, any number of threads can hold a read lock.
2) You can assign a write lock only if no thread holds a read or write lock.

In short, all threads can be read as long as no threads are written, but threads must be read and no threads are written.

When a read lock is held on a thread, another thread requests a write lock that is blocked, and if there are subsequent requests for a read lock, there are two strategies:
1) The subsequent read lock requests are passed, may cause the read lock is continuously allocated, the write lock application is always blocked, "starved" the write process.
2) Subsequent read lock requests are blocked, and the write lock is prioritized when the current holding read lock is closed.

When read access to protected data is more frequent than write access, the read-write lock provides a higher degree of concurrency than a normal mutex.

3. Record lock

Record locking is an extended type of read-write lock that can be used to share the read and write of a file between relatives or unrelated cities.

The function that performs the lock is FCNTL, the lock is maintained by the kernel, and its owner is identified by the process ID .

Features: Only used for locking between different processes, not between different threads within the same process.

The Unix kernel does not record this concept, and the interpretation of records is carried out by the application of read-write files. Each record is a byte range in the file.

When using Fcntl record lock, waiting for the reader priority or waiting for the writer priority is not guaranteed.

4. Signal Volume

Semaphores are primitives used to synchronize between different processes, or between different threads within a given process.
3 Semaphore Type:
1) POSIX-known semaphores: Use the POSIX IPC name identifier, which can be used for synchronization between processes or threads. (Can be used for inter-process relationships that are unrelated to each other)
2) POSIX memory-based semaphore (nameless semaphore): stored in a shared memory area that can be used for synchronization between processes or threads. (Not available for non-affinity inter-process)
3) System v Semaphore: maintained in the kernel and can be used for synchronization between processes or threads.

The POSIX semaphore does not have to be maintained in the kernel (the System v Semaphore is maintained by the kernel) and is identified by the name that may be the path name.

4.1 POSIX signal Volume

1. Overview

Three basic operations:
1) Create: Specify the initial value.
2) Wait: If the value is less than or equal to 0 is blocked, otherwise it is reduced by one, also called P operation.
3) hang out (POST): The value of the semaphore is added 1, plus if the value is greater than 0, wake up a thread that is blocking on the wait, also known as the V operation.

The wait and post of the semaphore are similar to the wait and signal of the condition variable, except that the operation of the semaphore is always remembered (which affects subsequent operations) because it permanently alters the value of the semaphore; the signal of the condition variable if no thread is waiting, The signal will be lost (no effect on subsequent operations).

The mutex is optimized for locking, the condition variable is waiting for optimization, the semaphore can be locked and wait, so the overhead is greater.

2, two-value signal volume
The value of a binary semaphore is 0 or 1, the resource is locked and the semaphore value is 0, and if the resource is available, the semaphore value is 1.

A binary semaphore can be used for mutual exclusion, just like a mutex. But the mutex must be threads unlocked by the line that locks it, and the semaphore is hung without having to be executed by the same thread that performed the wait operation.

Binary semaphores are used for producer consumer issues: Consider a producer that puts an entry into a buffer, and a simplified type that takes away a consumer of that entry.

3. Counting Signal Quantity
The value is between 0 and a certain limit (32767), the number of resources can be counted, the value of the semaphore is the number of available resources. The wait operation waits for the semaphore value to become greater than 0 (which means it is available) and then minus 1, and the hang-out operation simply adds a semaphore value of 1 (increasing the number of available resources) and wakes up waiting for the semaphore value to become any thread greater than 0.

4.2 System v Signal Volume

The System v Semaphore adds another level of complexity.

* * Count Semaphore set: one or more semaphores (constituting a set), each of which is a count semaphore. **system v semaphores generally refer to the set of counting semaphores. POSIX semaphores, in general, refer to a single count semaphore.

5. Differences in semaphore, mutex, and condition variables

The intent of semaphores is to synchronize between processes, which may or may not share the memory area, and the intent of the mutex and condition variables is to synchronize between threads, which always share the memory area, but the semaphore can also be used between threads, and the mutex and condition variables can also be used between processes.

1) The mutex is always threads unlocked by the line to which he is locked, and the semaphore can be hung out by other threads.
2) The mutex is either locked or unlocked (a binary state, similar to a two-value semaphore).
3) The semaphore has a state (value) associated with it, and the semaphore's hang-out operation is always remembered. When signaling to a condition variable, however, if no thread is waiting, the signal is lost.

Refer to "UNIX Network Programming: Volume 2"

UNIX Network Programming-synchronization

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.