memory systems that guarantee predictable ordering of memory accesses. A mutex is one general solution to this sort of problem. If each thread locks a mutex around the section of code that's using shared data, only one thread will be able to enter the section at a time.

Figure 3.2 shows a timing diagram of three threads sharing a mutex. Sections of the lines that are above the rounded box labeled 'mutex' show where the associated thread does not own the mutex. Sections of the lines that are below the center line of the box show where the associated thread owns the mutex, and sections of the lines hovering above the center line show where the thread is waiting to own the mutex.

Initially, the mutex is unlocked. Thread 1 locks the mutex and, because there is no contention, it succeeds immediately—thread l's line moves below the center

FIGURE 3.2 Mutex operation

of the box. Thread 2 then attempts to lock the mutex and, because the mutex is already locked, thread 2 blocks, its line remaining above the center line. Thread 1 unlocks the mutex, unblocking thread 2, which then succeeds in locking the mutex. Slightly later, thread 3 attempts to lock the mutex, and blocks. Thread 1 calls pthread_mutex_trylock to try to lock the mutex and, because the mutex is locked, returns immediately with EBUSY status. Thread 2 unlocks the mutex, which unblocks thread 3 so that it can lock the mutex. Finally, thread 3 unlocks the mutex to complete our example.

3.2.1 Creating and destroying a mutex

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; int pthread_mutex_init (

pthread_mutex_t *mutex, pthread_mutexattr_t *attr); int pthread_mutex_destroy (pthread_mutex_t *mutex);

A mutex is represented in your program by a variable of type pthread_mutex_t. You should never make a copy of a mutex, because the result of using a copied mutex is undefined. You can, however, freely copy a pointer to a mutex so that various functions and threads can use it for synchronization.

Most of the time you'll probably declare mutexes using extern or static storage class, at 'file scope,' that is, outside of any function. They should have 'normal' (extern) storage class if they are used by other files, or static storage class if used only within the file that declares the variable. When you declare a static mutex that has default attributes, you should use the PTHREAD_MUTEX_ INITIALIZER macro, as shown in the mutex_static.c program shown next. (You can build and run this program, but don't expect anything interesting to happen, since main is empty.)

¦ mutex_static.c

1 #include <pthread.h>

2 #include 'errors.h'

3

4 /*

5 * Declare a structure, with a mutex, statically initialized. This

6 * is the same as using pthread_mutex_init, with the default

7 * attributes.

8 */

9 typedef struct my_struct_tag {

10 pthread_mutex_t  mutex; /* Protects access to value */

11 int       value; /* Access protected by mutex */

12 } my_struct_t; 13

14 my_struct_t data = {PTHREAD_MUTEX_INITIALIZER, 0};

15

16 int main (int argc, char *argv[])

17 {

18 return 0;

19 }

Often you cannot initialize a mutex statically, for example, when you use malloc to create a structure that contains a mutex. Then you will need to call pthread_mutex_init to initialize the mutex dynamically, as shown in mutex_ dynamic.c, the next program. You can also dynamically initialize a mutex that you declare statically—but you must ensure that each mutex is initialized before it is used, and that each is initialized only once. You may initialize it before creating any threads, for example, or by calling pthread_once (Section 5.1). Also, if you need to initialize a mutex with nondefault attributes, you must use dynamic initialization (see Section 5.2.1).

¦ mutex_dynamic.c

1 #include <pthread.h>

2 #include 'errors.h' 3

4 /*

5 * Define a structure, with a mutex.

6 */

7 typedef struct my_struct_tag {

8 pthread_mutex_t mutex; /* Protects access to value */

9 int value; /* Access protected by mutex */ 10 } my_struct_t;

11

12 int main (int argc, char *argv[])

13 {

14 my_struct_t *data;

15 int status;

16

17 data = malloc (sizeof (my_struct_t));

18 if (data == NULL)

19 errno_abort ('Allocate structure');

20 status = pthread_mutex_init (&data->mutex, NULL);

21 if (status != 0)

22 err_abort (status, 'Init mutex');

23 status = pthread_mutex_destroy (&data->mutex);

24 if (status != 0)

25 err_abort (status, 'Destroy mutex');

26 (void)free (data);

27 return status;

28 }

It is a good idea to associate a mutex clearly with the data it protects, if possible, by keeping the definition of the mutex and data together. In mutex_static.c and mutex_dynamic.c for example, the mutex and the data it protects are defined in the same structure, and line comments document the association.

When you no longer need a mutex that you dynamically initialized by calling pthread_mutex_init, you should destroy the mutex by calling pthread_mutex_ destroy. You do not need to destroy a mutex that was statically initialized using

the PTHREAD_MUTEX_INITIALIZER macro.

You can destroy a mutex as soon as you are sure no threads are blocked on the mutex.

It is safe to destroy a mutex when you know that no threads can be blocked on the mutex, and no additional threads will try to lock the mutex. The best way to know this is usually within a thread that has just unlocked the mutex, when program logic ensures that no threads will try to lock the mutex later. When a thread locks a mutex within some heap data structure to remove the structure from a list and free the storage, for example, it is safe

Добавить отзыв
ВСЕ ОТЗЫВЫ О КНИГЕ В ИЗБРАННОЕ

0

Вы можете отметить интересные вам фрагменты текста, которые будут доступны по уникальной ссылке в адресной строке браузера.

Отметить Добавить цитату