2. Thread synchronization
POSIX provides two primitives for thread synchronization, which are mutually exclusive and conditional variables, respectively. Mutual exclusion is a simple original language that controls access to shared resources to prevent conflicts. About multithreaded programming, one thing is worth noting, that is, the address space of the entire program has all thread sharing. The result is that almost all resources can be shared - such as global variables, file descriptors, and the like. On the other hand, we will define some private local variables within the other functions of each thread, and other functions called by the function. In multi-threaded programs, global variables and local variables are always mixed. To make multiple threads run smoothly, the access to shared resources must be controlled.
The following is a producer / consumer program. Producers and consumers' access to the shared buffer is controlled by mutual exclusion. void * reader_function (void *); void * writer_function (void *); char buffer; int buffer_has_item = 0; pthread_mutex_t mutex; struct timespec delay; main () {pthread_t reader; delay.tv_sec = 2; delay.tv_nsec = 0; pthread_mutex_init (& mutex, pthread_mutexattr_default); pthread_create (& reader, pthread_attr_default, reader_function, NULL); writer_function ();} void * writer_function (void *) {while (1) {pthread_mutex_lock (& mutex); if (buffer_has_item == 0) {buffer = make_new_item (); buffer_has_item = 1;} pthread_mutex_unlock (& mutex); pthread_delay_np (& delay);}} void * reader_function (void *) {while (1) {pthread_mutex_lock (& mutex); if (buffer_has_item == 1) {consume_item ( Buffer; buffer_has_item = 0;} Pthread_Mutex_Unlock (& Mutex PTHREAD_DELAY_NP (& DELAY);}}
In this simple example, the shared buffer can only save a shared data item. Therefore, the buffer only has two states: "Yes" / "None". The producer first will lock the mutually exclusive to the buffer, and if the mutual exclusion has been locked, the producer will block until mutually exclusive unlock. After the producer locks the mutual exclusion, it will check if the buffer is empty (through the flag variable buffer_has_item). If the buffer has no data, the producer will generate a new data item into the buffer, and set the flag variable to make the consumer can know if they can make consumption. Next, the producer relieves the lock and waits for the mutual exclusion, so that consumers should have sufficient time to access the buffer. Consumers have adopted similar processes to access buffers. It first locks the mutual exclusion, checks the logo variable, if possible, consumes only the data item. The consumer will then unlock the mutual exclusion and wait for a small party to make the producer have time to write new data items.
In the above example, producers and consumers will continue to operate, continuous production, consumption. In fact, in a usual program, if you determine that you no longer use a mutual exclusion, you should use pthread_mutex_destroy (& Mutex) to destroy it. By the way, it should be initialized using pthread_mutex_init () before using a mutual exclusion. In our example, two parameters are used when initialization, the first mutual exclusion used to specify the initialized, the second is the mutual extension. (On DEC OSF / 1, the mutually exclusive properties are not practical, usually use pthread_mutexattr_default)
Correct use of mutual exclusion can effectively reduce competitive conditions. In fact, mutual exclusion is very simple, only two status: lock, not locked. It can be achieved by it. POSIX also provides a powerful tool to supplement the insufficient mutual exclusion. Using condition variables, a thread can be blocked and waited for wake-up signals in the case of locked mutually exclusive, while other threads can access the locked shared resources. When another thread is sent, the blocked thread will be awakened and can also access the shared resources that are locked before blocking. As a result, the combination of mutual exclusion and conditional variables can help us avoid the recirculation of the deadlock. We designed a library with only a single integer signal light using the mutual exclusion and condition variables. The source code of the library can be found in Appendix A. Description of condition variables can be found in Man Pages.
The functions involved in this section: pthread_mutex_init (), PTHREAD_MUTEX_LOCK (), pthread_mutex_unlock (), and pthread_mutex_destroy ().