Getting Started With POSIX Threads

  1. Introduction: What is the thread?    Why use thread? 

  Thread usually known as lightweight Itinerary (Lightweight process; LWP), this argument appears to be some simple, but it is a good starting point; thread UNIX process is the next of kin, but not identical.    To illustrate what thread, we must first understand UNIX process and Mach task, the relationship between the thread.    In UNIX, a process includes an implementation of the procedures, and some of his required system resources, such as file description table and address space.    However, in Mach, a task only, including a number of system resources; thread mastered by all implementation activities.    Mach task may have an arbitrary number of thread, and the Mach thread in the system belong to some task.    Belong to the same task of all thread owned by sharing the task of system resources.    Therefore, the thread is in essence a program counter and a stack together with a group of cache.    A UNIX process can be seen as only one thread of a Mach task. 

  Compared with the UNIX process, the thread is very petite Linglong, so the CPU, have a thread is a relatively inexpensive work.    On the other hand, the thread shared system resources with the monopoly system resources than the process, a thread is also save memory.    Mach thread let programmers will be able to make it convenient to the implementation in a single or multi-processor environment at the same time the implementation of the procedure.    No need to consider the question of the amount of processor, and direct access to multi-processing performance (if the number of processors).    In addition even in a single CPU environment, if the procedures are often the kind of 'rest', such as file and the socket I / O, or to provide thread on the effectiveness of the promotion. 

  Will be presented following some simple POSIX thread, and his DEC OSF / 1 OS, V3.0.'s Version (universe: I was solaris 2.5.1 and / SunOS 4.1.4 on the test! Almost.) POSIX thread referred to as pthread, he and the non-POSIX cthread very similar. 

  2.Hello World 
  Less nonsense, let's get started!    Pthread_create function of the establishment of a thread.    He needs four parameters: thread variables of thread, a thread description of this function for function and parameters.    For example: 

  Pthread_t a_thread; 
  Pthread_attr_t a_thread_attribute; 
  Void thread_function (void * argument); 
  Char * some_argument; 
  Pthread_create (& a_thread, a_thread_attribute, (void *) & thread_function, 
  (Void *) & some_argument); 

  Thread attribute describes some of the thread, we only want to use him at least need to specify how the thread stack.    In the future there will be many interesting thread attribute, but the present moment, a simple majority of the procedures specified pthread_attr_default on it.    Unlike process, the need to use fork () system call to the child process and his parents started at the same time, specified in the thread from the pthread_create thread_function begun to be implemented.    The reasons are simple: If the thread is not a separate place from the beginning of implementation, would result in a pile of thread to use the same system resources for the implementation of the same instruction.    Remember?    Thread is the 'sharing' system resources.    (Universe: to stop here, recall that the process is how … ^ _ ^) 

  In the know how to create a thread, we can begin the process of a thread!    To the design of a multi-thread version of the printf ( "Hello world \ n");!    At first, we need to thread the two variables, and a thread function, and also to be able to tell different thread print different message.    I want to let different thread print "hello world", the two different parts of "hello" and "world."    Procedure looks like this: 

  Void print_message_function (void * ptr); 
  Main () 
  ( 
  Pthread_t thread1, thread2; 
  Char * message1 = "Hello"; 
  Char * message2 = "World"; 

  Pthread_create (& thread1, pthread_attr_default, 
  (Void *) & print_message_function, (void *) message1); 
  Pthread_create (& thread2, pthread_attr_default, 
  (Void *) & print_message_function, (void *) message2); 
  Exit (0); 
  ) 

  Void print_message_function (void * ptr) 
  ( 
  Char * message; 
  Message = (char *) ptr; 
  Printf ( "% s", message); 
  ) 

  Pthread_create print_message_function attention to the parameters and the parameters of his message1, message2, this procedure with a pthread_create produce the thread, and "Hello" as its initial parameters, followed a second thread, its initial parameters designated as "World . "    The first thread started when activated, print_message_function implemented, the introduction of its "Helllo."    He will be "Hello" print-out, and then ended.    The second thread is doing similar things: print "World."    Seem very reasonable, but this process has two main flaws. 

  The first shortcoming, since the two thread is at the same time, so we can not guarantee that the first thread to printf will be the implementation of that trip, on the screen might see "Hello World", but also might see the "World Hello" .    In addition, in the main (parent thread), the exit call the whole process will come to an end, all this will lead to the end of the thread.    Therefore, if the exit before the implementation of the printf, there will be no output produced.    In fact, in any thread (or parent or child), call exit would lead to the end process, and all the thread also followed with an end.    So if the end of a thread, we must use this function pthread_exit. 

  In our small programs, there are two competitive conditions (race condition), a first look at the implementation of the parent process to exit?    Or the child process to the implementation of the printf?    Second, there are two child thread in the end is who first printed message?    In order to let us hope that the procedures in accordance with the order, we have attempted to force each thread interaction of waiting, the process by adding the following two sleep pursuit of this objective. 

  Void print_message_function (void * ptr); 

  Main () 
  ( 
  Pthread_t thread1, thread2; 
  Char * message1 = "Hello"; 
  Char * message2 = "World"; 

  Pthread_create (& thread1, pthread_attr_default, 
  (Void *) & print_message_function, (void *) message1); 
  Sleep (10); / / The rest, such as "Hello" print-out to produce a thread 

  Pthread_create (& thread2, pthread_attr_default, 
  (Void *) & print_message_function, (void *) message2); 
  Sleep (10); / / The rest, such as "World" print-out to the end. 

  Exit (0); 
  ) 

  Void print_message_function (void * ptr) 
  ( 
  Char * message; 
  Message = (char *) ptr; 
  Printf ( "% s", message); 
  Pthread_exit (0); 
  ) 

  This process reached our goal?    Not entirely due to the use of timming delay to reach the inter-thread synchronization is wrong, because of the close coupling thread (tightly coupled) characteristics makes it easy for us to use some of the imprecise way to reach a deal with the same period of sync, but we should not do so.    In this process we have encountered conditions of competition and distributed applications, sharing of resources of the same.    Sharing resources for the standard output, and scattered into the principles of the three procedures thread.    The first thread in the second thread must be used before printf / stdout, and both must exit before the parent thread call to complete their work. 

  Apart from the use of delay to achieve synchronization effect, another error occurred in the sleep system calls; exit as the impact of the process, when thread call sleep, stresses the entire process stopped.    This means that all belong to this process with the thread will also come to a halt.    Therefore, in the above programs, call sleep apart from the slow process simply to 20 seconds, does not have any additional impact.    Another application is a function of pthread_delay_np (np said not process).    For example, let thread pause two seconds, with the following procedures: 

  Struct timespec delay; 
  Delay.tv_sec = 2; 
  Delay.tv_nsec = 0; 
  Pthread_delay_np (& delay); 

  That the function of this section are: pthread_create (), 
  Pthread_exit (), 
  Pthread_delay_np (). 

  3.Thread Synchronization 

  POSIX were used to provide the basic thread synchronization instructions: mutex and the condition variable.    Mutex refers to a group used to control access to shared resources of a group function.    Note that the use of thread in the circumstances, because the entire address space is shared, all things can be seen as the sharing of resources.    Under normal circumstances, the use of some thread in pthreadcreate before they call or in the definition of the function in the definition of variables to complete its work, and his results through the whole merger variables.    Of these, we can access variables, we need to be controlled. 

  The following is a reader / writer procedures, and procedures, there is a reader, a writer, they share a buffer, and mutex used to control access to the buffer. 

  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, (void *) & reader_function, 
NULL);
  Writer_function (); 
  ) 

  Void writer_function (void) 
  ( 
  While (1) 
  ( 
  Pthread_mutex_lock (& mutex); 
  If (buffer_has_item == 0) 
  ( 
  Make_new_item buffer = (); 
  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 procedure, we assume that the buffer capacity of only 1, there are two possibilities for this buffer state: 'there is a' no information or any information '.    Writer first mutex lock, if mutex has been locked, has been suspended until the mutex was unlocked.    And see whether the buffer is empty, if the buffer is' no information 'status, a writer produce new information will be placed in the buffer.    Buffer_has_item then set a flag to flag to reader may know, there is a data buffer.    Finally writer will unlock the mutex, 2 seconds and rest for a free reader can be removed to the information contained in the buffer.    Here with the use of the delay before the delay have completely different meanings, if not for this delay, then unlock mutex writer in the next directive is another T in order to produce new information, re-lock mutex.    This reader will have no opportunity to read the information in the buffer.    So here with a delay appears to be a good idea. 

  Writer and reader looks the same as it lock mutex first, and then see whether there are data in the buffer, if their information will be removed, and then unlock the mutex, and then two seconds delay, the writer an opportunity to Add new information.    In this example, the writer and reader on this has been on the run, constantly generated / remove the information in the buffer.    In other circumstances, we may no longer need to use the mutex, this can be used pthread_mutex_destroy (& mutex); to release mutex. 

  In the initial mutex time, we used to as mutex pthread_mutexattr_default characteristics.    In the OSF / 1, mutex of没啥useful, therefore, for enough. 

  Mutex tends to be used in resolving the issue of race condition, but mutex is not a very strong mechanism, because he only two states: locked and unlocked.    POSIX condition variable (condition variable) will be extended mutex function, can do to a certain thread can be suspended and waiting for another thread signal (signal).    When the signal came, the thread wake up, and then related mutex lock up.    This approach can solve the reader / writer in the process of spin-lock problem.    Appendix A has a condition variable use mutex and made a simple integer semaphores.    Details of the condition variable usage can refer man page. 

  That the function of this section are: pthread_mutex_init (), 
  Pthread_mutex_lock (), 
  Pthread_mutex_unlock (), 
  Pthread_mutex_destroy (). 

  4. Reached coordination of the use of Semaphores 

  (This section used the Semapore function of strange, so I am not in general use. Look if we write this, we must use the library in the appendix.) 

  Next we want to use semaphore to rewrite the section on the reader / writer procedures.    Integer with more intrepid semaphore to replace the mutex, spin-lock and resolve the problem.    Semaphore associated with the operation have semaphore_up, semaphore_down, semaphore_init, semaphore_destroy and semaphore_decrement. Semaphore_down which semaphore_up and semaphore, and the traditional grammar same - down operations in the semaphore value of less than or equal to zero when the suspension.    And the incremental computing up semaphore.    In the use of semaphore must call before the init function, and all the initial values are semaphore 1.    When the semaphore are no longer used, destroy its function can be released.    All these functions need only one parameter: a semaphore object indicators point. 

  Semaphore_decrement is a non-blocking function he will semaphore reduced to a negative value, this method is what is the use?    Generally used in the initial set when a semaphore its initial value.    Later on, we will give an example.    First look at the next version of the semaphore reader / writer procedures. 

  Void reader_function (void); 
  Void writer_function (void); 

  Char buffer; 
  Semaphore writers_turn; 
  Semaphore readers_turn; 

  Main () 
  ( 
  Pthread_t reader; 

  Semaphore_init (& readers_turn); 
  Semaphore_init (& writers_turn); 

  / * Writer must go first * / 
  Semaphore_down (& readers_turn); 

  Pthread_create (& reader, pthread_attr_default, 
  (Void *) & reader_function, NULL); 
  Writer_function (); 
  ) 

  Void writer_function (void) 
  ( 
  While (1) 
  ( 
  Semaphore_down (& writers_turn); 
  Make_new_item buffer = (); 
  Semaphore_up (& readers_turn); 
  ) 
  ) 

  Void reader_function (void) 
  ( 
  While (1) 
  ( 
  Semaphore_down (& readers_turn); 
  Consume_item (buffer); 
  Semaphore_up (& writers_turn); 
  ) 
  ) 

  The above examples demonstrate yet End before integer semaphore power.    Then we will be amended in section II of the Hello World program, and use semaphore to amend their race conditions problem. 

  Void print_message_function (void * ptr); 

  Semaphore child_counter; 
  Semaphore worlds_turn; 

  Main () 
  ( 
  Pthread_t thread1, thread2; 
  Char * message1 = "Hello"; 
  Char * message2 = "World"; 

  Semaphore_init (& child_counter); 
  Semaphore_init (& worlds_turn); 

  Semaphore_down (& worlds_turn) / * world goes second * / 

  Semaphore_decrement (& child_counter) / * value now 0 * / 
  Semaphore_decrement (& child_counter) / * value now -1 * / 
  / * 
  * Child_counter now must be up-ed 2 times for a thread blocked on it 
  * To be released 
*
  * / 

  Pthread_create (& thread1, pthread_attr_default, 
  (Void *) & print_message_function, (void *) message1); 

  Semaphore_down (& worlds_turn); 

  Pthread_create (& thread2, pthread_attr_default, 
  (Void *) & print_message_function, (void *) message2); 

  Semaphore_down (& child_counter); 

  / * Not really necessary to destroy since we are exiting anyway * / 
  Semaphore_destroy (& child_counter); 
  Semaphore_destroy (& worlds_turn); 
  Exit (0); 
  ) 

  Void print_message_function (void * ptr) 
  ( 
  Char * message; 
  Message = (char *) ptr; 
  Printf ( "% s", message); 
  Fflush (stdout); 
  Semaphore_up (& worlds_turn); 
  Semaphore_up (& child_counter); 
  Pthread_exit (0); 
  ) 

  Easily can be seen, the above procedures and did not race condition, but also in accordance with the results printed in the correct order.    Semaphore child_counter the purpose of which is to allow parent thread suspended until all the children and the implementation of printf followed semaphore_up (& child_counter). 

  That the function of this section are: semaphore_init (), semaphore_up (), 
  Semaphore_down (), semaphore_destroy (), 
  Semaphore_decrement (). 

  5. Practical use 

  Compile use pthread procedures must include the header file relevant (universe: general pthread.h) and links pthread library: 

  Cc hello_world.c-o hello_world-lpthreads 
  (Alpha, you want to add - lc_r) 
  (Universe: solaris on the use - or lthread - lpthread can.) 

  If you want to use semaphore you must also use the relevant header file and library. 

  DEC pthread is based on the POSIX IV of the thread rather than the standard developed by the POSIX VIII.    Pthread_join function allows a thread wait for another designated thread to the end of the thread.    So in the Hello World program can be used to determine whether the children thread end.    But DEC, this function is not reliable, in the paragraph following procedures, if the designated some_thread did not exist, he will lead to mistakes rather than direct return. 

  Pthread_t some_thread; 
  Void * exit_status; 
  Pthread_join (some_thread, & exit_status); 

  Some other strange error may occur in places other than the thread function, but the reason for this.    In our example, and not to check thread function is correctly carried out, but this is necessary.    Almost all are pthread function in the event of errors return -1.    For example: 

  Pthread_t some_thread; 
  If (pthread_create (& some_thread, …) == -1) 
  ( 
  Perror ( "Thread creation error"); 
  Exit (1); 
  ) 

  Semaphore library in the event of the wrong time will be printed and left some messages. 

  Not cited in the text, but with some Manwei function as follows. 

  Pthread_yield (); notice scheduler thread to transfer his executive powers, 
  No need parameters. 

  Pthread_t me; 
  Me = pthread_self (); to make his own thread identifier. 

  Pthread_t thread; 
  Pthread_detach (thread); notice in the library behind the pthread_join call, not 
  Exit status, and enhance the efficiency of thread. 

  Appendix A - Semaphore Library Code 

  ================================================== ============================ 
  Semaphore.h follows 
  ================================================== ============================ 

  /************************************************* *************************** \ 
*
  * Written by 
  * Tom Wagner (wagner@cs.umass.edu) 
  * At the Distributed Problem Solving Lab 
  * Department of Computer Science, University of Massachusetts, 
  * Amherst, MA 01003 
*
  * Copyright (c) 1995 UMASS CS Dept. All rights are reserved. 
*
  * Development of this code was partially supported by: 
  * ONR grant N00014-92-J-1450 
  * NSF contract CDA-8922572 
*
  * ————————————————- ————————– 
*
  * This code is free software; you can redistribute it and / or modify it. 
  * However, this header must remain intact and unchanged. Additional 
  * Information may be appended after this header. Publications based on 
  * This code must also include an appropriate reference. 
*
  * This code is distributed in the hope that it will be useful, but 
  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 
  * Or FITNESS FOR A PARTICULAR PURPOSE. 
*
  \ ************************************************* ***************************/ 

  # Ifndef SEMAPHORES 
  # Define SEMAPHORES 

  # Include 
  # Include 

  Typedef struct Semaphore 
  ( 
  Int v; 
  Pthread_mutex_t mutex; 
  Pthread_cond_t cond; 
  ) 
Semaphore;

  Int semaphore_down (Semaphore * s); 
  Int semaphore_decrement (Semaphore * s); 
  Int semaphore_up (Semaphore * s); 
  Void semaphore_destroy (Semaphore * s); 
  Void semaphore_init (Semaphore * s); 
  Int semaphore_value (Semaphore * s); 
  Int tw_pthread_cond_signal (pthread_cond_t * c); 
  Int tw_pthread_cond_wait (pthread_cond_t * c, pthread_mutex_t * m); 
  Int tw_pthread_mutex_unlock (pthread_mutex_t * m); 
  Int tw_pthread_mutex_lock (pthread_mutex_t * m); 
  Void do_error (char * msg); 

  # Endif 

  ================================================== ============================ 
  Semaphore.c follows 
  ================================================== ============================ 

  /************************************************* *************************** \ 
*
  * Written by 
  * Tom Wagner (wagner@cs.umass.edu) 
  * At the Distributed Problem Solving Lab 
  * Department of Computer Science, University of Massachusetts, 
  * Amherst, MA 01003 
*
  * Copyright (c) 1995 UMASS CS Dept. All rights are reserved. 
*
  * Development of this code was partially supported by: 
  * ONR grant N00014-92-J-1450 
  * NSF contract CDA-8922572 
*
  * ————————————————- ————————– 
*
  * This code is free software; you can redistribute it and / or modify it. 
  * However, this header must remain intact and unchanged. Additional 
  * Information may be appended after this header. Publications based on 
  * This code must also include an appropriate reference. 
*
  * This code is distributed in the hope that it will be useful, but 
  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 
  * Or FITNESS FOR A PARTICULAR PURPOSE. 
*
  \ ************************************************* ***************************/ 

  # Include "semaphore.h" 

  / * 
  * Function must be called prior to semaphore use. 
*
  * / 
  Void 
  Semaphore_init (Semaphore * s) 
  ( 
  S-> v = 1; 
  If (pthread_mutex_init (& (s-> mutex), pthread_mutexattr_default) == -1) 
  Do_error ( "Error setting up semaphore mutex"); 

  If (pthread_cond_init (& (s-> cond), pthread_condattr_default) == -1) 
  Do_error ( "Error setting up semaphore condition signal"); 
  ) 

  / * 
  * Function should be called when there is no longer a need for 
  * The semaphore. 
*
  * / 
  Void 
  Semaphore_destroy (Semaphore * s) 
  ( 
  If (pthread_mutex_destroy (& (s-> mutex)) == -1) 
  Do_error ( "Error destroying semaphore mutex"); 

  If (pthread_cond_destroy (& (s-> cond)) == -1) 
  Do_error ( "Error destroying semaphore condition signal"); 
  ) 

  / * 
  * Function increments the semaphore and signals any threads that 
  * Are blocked waiting a change in the semaphore. 
*
  * / 
  Int 
  Semaphore_up (Semaphore * s) 
  ( 
  Int value_after_op; 

  Tw_pthread_mutex_lock (& (s-> mutex)); 

  (S-> v) + +; 
  Value_after_op = s-> v; 

  Tw_pthread_mutex_unlock (& (s-> mutex)); 
  Tw_pthread_cond_signal (& (s-> cond)); 

  Return (value_after_op); 
  ) 

  / * 
  * Function decrements the semaphore and blocks if the semaphore is 
  * <= 0 until another thread signals a change. 
*
  * / 
  Int 
  Semaphore_down (Semaphore * s) 
  ( 
  Int value_after_op; 

  Tw_pthread_mutex_lock (& (s-> mutex)); 
  While (s-> v <= 0) 
  ( 
  Tw_pthread_cond_wait (& (s-> cond), & (s-> mutex)); 
  ) 

  (S-> v) -; 
  Value_after_op = s-> v; 

  Tw_pthread_mutex_unlock (& (s-> mutex)); 

  Return (value_after_op); 
  ) 

  / * 
  * Function does NOT block but simply decrements the semaphore. 
  * Should not be used instead of down - only for programs where 
  * Multiple threads must up on a semaphore before another thread 
  * Can go down, ie, allows programmer to set the semaphore to 
  * A negative value prior to using it for synchronization. 
*
  * / 
  Int 
  Semaphore_decrement (Semaphore * s) 
  ( 
  Int value_after_op; 

  Tw_pthread_mutex_lock (& (s-> mutex)); 
  S-> v -; 
  Value_after_op = s-> v; 
  Tw_pthread_mutex_unlock (& (s-> mutex)); 

  Return (value_after_op); 
  ) 

  / * 
  * Function returns the value of the semaphore at the time the 
  * Critical section is accessed. Obviously the value is not guarenteed 
  * After the function unlocks the critical section. Provided only 
  * For casual debugging, a better approach is for the programmar to 
  * Protect one semaphore with another and then check its value. 
  * An alternative is to simply record the value returned by semaphore_up 
  * Or semaphore_down. 
*
  * / 
  Int 
  Semaphore_value (Semaphore * s) 
  ( 
  / * Not for sync * / 
  Int value_after_op; 

  Tw_pthread_mutex_lock (& (s-> mutex)); 
  Value_after_op = s-> v; 
  Tw_pthread_mutex_unlock (& (s-> mutex)); 

  Return (value_after_op); 
  ) 

  / * ———————————————— ——————– * / 
  / * The following functions replace standard library functions in that * / 
  / * They exit on any error returned from the system calls. Saves us * / 
  / * From having to check each and every call above * / 
  / * ———————————————— ——————– * / 

  Int 
  Tw_pthread_mutex_unlock (pthread_mutex_t * m) 
  ( 
  Int return_value; 

  If ((pthread_mutex_unlock return_value = (m)) == -1) 
  Do_error ( "pthread_mutex_unlock"); 

  Return (return_value); 
  ) 

  Int 
  Tw_pthread_mutex_lock (pthread_mutex_t * m) 
  ( 
  Int return_value; 

  If ((pthread_mutex_lock return_value = (m)) == -1) 
  Do_error ( "pthread_mutex_lock"); 

  Return (return_value); 
  ) 

  Int 
  Tw_pthread_cond_wait (pthread_cond_t * c, pthread_mutex_t * m) 
  ( 
  Int return_value; 

  If ((return_value = pthread_cond_wait (c, m)) == -1) 
  Do_error ( "pthread_cond_wait"); 

  Return (return_value); 
  ) 

  Int 
  Tw_pthread_cond_signal (pthread_cond_t * c) 
  ( 
  Int return_value; 

  If ((return_value = pthread_cond_signal (c)) == -1) 
  Do_error ( "pthread_cond_signal"); 

  Return (return_value); 
  ) 

  / * 
  * Function just prints an error message and exits 
*
  * / 
  Void 
  Do_error (char * msg) 
  ( 
  Perror (msg); 
  Exit (1); 
  ) 

Bookmark it: These icons link to social bookmarking sites where readers can share and discover new web pages.
  • Digg
  • Sphinn
  • del.icio.us
  • Google
  • DotNetKicks
  • DZone
  • Furl
  • Netvouz

Releated Articles

  • Popuklar Articles

0 Comments to “Getting Started With POSIX Threads”

No Comments. Send your comment.

Leave a Reply

You must be logged in to post a comment.