POSIX thread for Linux Network Programming (III): POSIX semaphore and mutex lock example producer-consumer issues

Source: Internet
Author: User

I. POSIX semaphores

For more information about semaphores, see here. System
V semaphores, now POSIX semaphores.

System V semaphores can only be used for synchronization between processes, while POSIX semaphores can be used for synchronization between processes. System V semaphore each PV operation can be N, but POSIX semaphore each PV can only be 1. In addition, POSIX semaphores are named and anonymous (MAN 7 sem_overview ):

1. Named semaphores

The name can be distinguished in the form of/somename. Only one/can exist, and the total length cannot exceed name_max-4 (generally 251 ).

You need to use the sem_open function to create or open the file. The PV operations are sem_wait and sem_post. You can use sem_close to close the file and use sem_unlink to delete the file.

2. Anonymous semaphores

It is stored in a shared memory. For thread sharing, this region can be a global variable. For process sharing, it can be System V shared memory (created by shmget and shmat ing ), it can also be POSIX shared memory (shm_open creation, MMAP ing ).

The anonymous semaphores must be initialized using sem_init. One of the parameters of sem_init determines whether thread sharing or process sharing. You can also use sem_post and sem_wait to perform operations. Before the shared memory is released, sem_destroy must be used for anonymous semaphores
Destroy.

The specific parameters of these functions can be man.


Ii. mutex lock

For multi-threaded programs, access conflicts are common. The solution is to introduce mutex and mutualexclusive locks ), the thread that acquires the lock can complete the "Read-Modify-write" Operation and release the lock to other threads. The thread that does not obtain the lock can only wait but cannot access the shared data, in this way, the "Read-Modify-write" three steps constitute an atomic operation, either executed or not executed, and will not be interrupted in the middle, this operation will not be performed concurrently on other processors.

Mutex is represented by a variable of the pthread_mutex_t type. The pthread_mutex_init function initializes mutex. the ATTR parameter sets the mutex attribute. If ATTR is null, it indicates the default attribute. For details, refer to the struct:

C ++ code
1
2
3
4
5
6
7
8
9
10
Struct pthread_mutexattr_t
{
Enum lock_type // use pthread_mutexattr_settype to change
{
Pthread_mutex_timed_np [Default] // when a thread locks, other threads requesting the lock form a waiting queue. After unlocking, the lock is obtained by priority.
Pthread_mutex_adaptive_np // The simplest lock type of the action. After unlocking, all threads will compete again.
Pthread_mutex_recursive_np // The same thread is allowed to successfully obtain the same lock multiple times. Of course, you also need to unlock it multiple times. Other threads re-compete When unlocking.
Pthread_mutex_errorcheck_np // if the same thread requests the same lock, edeadlk is returned. Otherwise, the action is the same as that of pthread_mutex_timed_np.
} Type;
} ATTR;

The mutex initialized by the pthread_mutex_init function can be destroyed by pthread_mutex_destroy. If the mutex variable is statically allocated (Global or static), you can also use a macro to define pthread_mutex_initializer for initialization, which is equivalent to pthread_mutex_init for initialization and the ATTR parameter is null.

One thread can call pthread_mutex_lock to obtain the mutex. If another thread has already called pthread_mutex_lock to obtain the mutex, the current thread needs to wait until another thread calls pthread_mutex_unlock to release the mutex. The current thread is awakened, to obtain the mutex and continue the execution.

The specific function above can be man.


Iii. producer and consumer issues

For more information about producer and consumer problems, see here. The following uses POSIX semaphores and mutex lock for Demonstration:

C ++ code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
# Include <unistd. h>
# Include <sys/types. h>
# Include <pthread. h>
# Include <semaphore. h>

# Include <stdlib. h>
# Include <stdio. h>
# Include <errno. h>
# Include <string. h>

# Define err_exit (m )\
Do \
{\
Perror (m );\
Exit (exit_failure );\
} While (0)

# Define consumers_count 1
# Define producers_count 1
# Define buffsize 10

Int g_buffer [buffsize];

Unsigned short in = 0;
Unsigned short out = 0;
Unsigned short produce_id = 0;
Unsigned short consume_id = 0;

Sem_t g_sem_full;
Sem_t g_sem_empty;
Pthread_mutex_t g_mutex;

Pthread_t g_thread [consumers_count + producers_count];

Void * consume (void * Arg)
{
Int I;
Int num = (INT) ARG;
While (1)
{
Printf ("% d wait buffer not empty \ n", num );
Sem_wait (& g_sem_empty );
Pthread_mutex_lock (& g_mutex );

For (I = 0; I <buffsize; I ++)
{
Printf ("% 02d", I );
If (g_buffer [I] =-1)
Printf ("% s", "null ");
Else
Printf ("% d", g_buffer [I]);

If (I = out)
Printf ("\ t <-- consume ");

Printf ("\ n ");
}
Consume_id = g_buffer [out];
Printf ("% d begin consume product % d \ n", num, consume_id );
G_buffer [out] =-1;
Out = (out + 1) % buffsize;
Printf ("% d end consume product % d \ n", num, consume_id );
Pthread_mutex_unlock (& g_mutex );
Sem_post (& g_sem_full );
Sleep (1 );
}
Return NULL;
}

Void * produce (void * Arg)
{
Int num = (INT) ARG;
Int I;
While (1)
{
Printf ("% d wait buffer not full \ n", num );
Sem_wait (& g_sem_full );
Pthread_mutex_lock (& g_mutex );
For (I = 0; I <buffsize; I ++)
{
Printf ("% 02d", I );
If (g_buffer [I] =-1)
Printf ("% s", "null ");
Else
Printf ("% d", g_buffer [I]);

If (I = in)
Printf ("\ t <-- produce ");

Printf ("\ n ");
}

Printf ("% d begin produce product % d \ n", num, produce_id );
G_buffer [in] = produce_id;
In = (in + 1) % buffsize;
Printf ("% d end produce product % d \ n", num, produce_id ++ );
Pthread_mutex_unlock (& g_mutex );
Sem_post (& g_sem_empty );
Sleep (5 );
}
Return NULL;
}

Int main (void)
{
Int I;
For (I = 0; I <buffsize; I ++)
G_buffer [I] =-1;

Sem_init (& g_sem_full, 0, buffsize );
Sem_init (& g_sem_empty, 0, 0 );

Pthread_mutex_init (& g_mutex, null );

For (I = 0; I <consumers_count; I ++)
Pthread_create (& g_thread [I], null, consume, (void *) I );

For (I = 0; I <producers_count; I ++)
Pthread_create (& g_thread [consumers_count + I], null, produce, (void *) I );

For (I = 0; I <consumers_count + producers_count; I ++)
Pthread_join (g_thread [I], null );

Sem_destroy (& g_sem_full );
Sem_destroy (& g_sem_empty );
Pthread_mutex_destroy (& g_mutex );

Return 0;
}

Compared with the program here, the program logic does not change much, but it is replaced by pthread_mutex_lock.
Sem_mutex, followed by the synchronization between demo threads. Currently, the producer and consumer of the preceding program have one thread each, but the producer's sleep time is five times that of the consumer, so the consumer will often block in sem_wait (& g_sem_empty) as the buffer is often empty, you can change productors_count to 5, that is, five producer threads and one consumer thread, and the producer's sleep time is five times that of the consumer, from the dynamic output, we can see that there is basically a dynamic balance, that is, five producers have produced five items at once, and the consumer consumes one copy for 1 second, just before the producer continues production.


Iv. Introduction to spin locks and read/write locks

(1) spin locks

A spin lock is similar to a mutex lock, and has higher performance than a mutex lock.
A very important difference between a spin lock and a mutex lock is that when a thread applies for a spin lock, the thread will not be suspended, and it is in a busy waiting state, which is generally used in the case of a short wait time.
Pthread_spin_init
Pthread_spin_destroy
Pthread_spin_lock
Pthread_spin_unlock

(2) read/write locks

1. As long as no thread holds the given read/write lock for writing, any number of threads can hold the read/write lock for reading
2. Only when no thread holds a given read/write lock for reading or writing can the read/write lock be assigned for writing
3. read/write locks are used to read shared locks, while read/write locks are used to write exclusive locks.
Pthread_rwlock_init
Pthread_rwlock_destroy
Int pthread_rwlock_rdlock
Int pthread_rwlock_wrlock
Int pthread_rwlock_unlock


Refer:

Linux C Programming one-stop learning

UNP

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.