Книга: Programming with POSIX® Threads

5.1 One-time initialization

5.1 One-time initialization

"Tis the voice of the Jubjub!" he suddenly cried.

(This man, that they used to call "Dunce.")

"As the Bellman would tell you," he added with pride,

"I have uttered that sentiment once."

Lewis Carroll, The Hunting of the Snark

pthread_once_t once_control = PTHREAD_ONCE_INIT;

int pthread_once (pthread_once_t *once_control,

void (*init_routine) (void));

Some things need to be done once and only once, no matter what. When you are initializing an application, it is often easiest to do all that from main, before calling anything else that might depend on the initialization — and, in particular, before creating any threads that might depend on having initialized mutexes, created thread-specific data keys, and so forth.

If you are writing a library, you usually don't have that luxury. But you must still be sure that the necessary initialization has been completed before you can use anything that needs to be initialized. Statically initialized mutexes can help a lot, but sometimes you may find this "one-time initialization" feature more convenient.

In traditional sequential programming, one-time initialization is often managed by a boolean variable. A control variable is statically initialized to 0, and any code that depends on the initialization can test the variable. If the value is still 0 it can perform the initialization and then set the variable to 1. Later checks will skip the initialization.

When you are using multiple threads, it is not that easy. If more than one thread executes the initialization sequence concurrently, two threads may both find initializer to be 0, and both perform the initialization, which, presumably, should have been performed only once. The state of initialization is a shared invariant that must be protected by a mutex.

You can code your own one-time initialization using a boolean variable and a statically initialized mutex. In many cases this will be more convenient than pthread_once, and it will always be more efficient. The main reason for pthread_ once is that you were not originally allowed to statically initialize a mutex. Thus, to use a mutex, you had to first call pthread_mutex_init. You must initialize a mutex only once, so the initialization call must be made in one-time initialization code. The pthread_once function solved this recursive problem. When static initialization of mutexes was added to the standard, pthread_once was retained as a convenience function. If it's convenient, use it, but remember that you don't have to use it.

First, you declare a control variable of type pthread_once_t. The control variable must be statically initialized using the PTHREAD_ONCE_INIT macro, as shown in the following program, called once.c. You must also create a function containing the code to perform all initialization that is to be associated with the control variable. Now, at any time, a thread may call pthread_once, specifying a pointer to the control variable and a pointer to the associated initialization function.

The pthread_once function first checks the control variable to determine whether the initialization has already completed. If so, pthread_once simply returns. If initialization has not yet been started, pthread_once calls the initialization function (with no arguments), and then records that initialization has been completed. If a thread calls pthread_once while initialization is in progress in another thread, the calling thread will wait until that other thread completes initialization, and then return. In other words, when any call to pthread_once returns successfully, the caller can be certain that all states initialized by the associated initialization function are ready to go.

13-20 The function once_init_routine initializes the mutex when called — the use of pthread_once ensures that it will be called exactly one time.

29 The thread function thread_routine calls pthread_once before using mutex, to ensure that it exists even if it had not already been created by main.

51 The main program also calls pthread_once before using mutex, so that the program will execute correctly regardless of when thread_routine runs. Notice that, while I normally stress that all shared data must be initialized before creating any thread that uses it, in this case, the only critical shared data is really the once_block — it is irrelevant that the mutex is not initialized, because the use of pthread_once ensures proper synchronization.

? once.c

1 #include <pthread.h>
2 #include "errors.h"

3
4 pthread_once_t once_block = PTHREAD_ONCE_INIT;
5 pthread_mutex_t mutex;

6
7 /*
8 * This is the one-time initialization routine. It will be
9 * called exactly once, no matter how many calls to pthread_once
10 * with the same control structure are made during the course of
11 * the program.
12 */
13 void once_init_routine (void)
14 {
15  int status;

16
17  status = pthread_mutex_init (&mutex, NULL);
18  if (status != 0)
19  err_abort (status, "Init Mutex");
20 }
21
22 /*
23 * Thread start routine that calls pthread_once.
24 */
25 void *thread_routine (void *arg)
26 {
27  int status;

28
29  status = pthread_once (&once_block, once_init_routine);
30  if (status != 0)
31  err_abort (status, "Once init");
32  status = pthread_mutex_lock (&mutex);
33  if (status != 0)
34  err_abort (status, "Lock mutex");
35  printf ("thread_routine has locked the mutex.n");
36  status = pthread_mutex_unlock (&mutex);
37  if (status != 0)
38  err_abort (status, "Unlock mutex");
39  return NULL;
40 }

41
42 int main (int argc, char *argv[])
43 {
44  pthread_t thread_id;
45  char *input, buffer[64];
46  int status;

47
48  status = pthread_create (&thread_id, NULL, thread_routine, NULL);
49  if (status != 0)
50  err_abort (status, "Create thread");
51  status = pthread_once (&once_block, once_init_routine);
52  if (status != 0)
53  err_abort (status, "Once init");
54  status = pthread_mutex_lock (&mutex);
55  if (status != 0)
56  err_abort (status, "Lock mutex");
57  printf ("Main has locked the mutex.n");
58  status = pthread_mutex_unlock (&mutex);
59  if (status != 0)
60  err_abort (status, "Unlock mutex");
61  status = pthread_join (thread_id, NULL);
62  if (status != 0)
63  err_abort (status, "Join thread");
64  return 0;
65 }

Оглавление книги


Генерация: 0.076. Запросов К БД/Cache: 0 / 0
поделиться
Вверх Вниз