A brief analysis of thread _c language in C + + programming

Source: Internet
Author: User
Tags function prototype mutex posix rand

The concept of threads

The text segment and data segment of threads in C + + are shared, and if a function is defined, it can be invoked in each thread, and if a global variable is defined, it can be accessed in each thread. In addition, the threads also share the following process resources and environments:

    • File descriptor
    • How each kind of signal is handled
    • Current working directory
    • User ID and Group ID

However, some resources are available for each thread:

    • Thread ID
    • Context, including values for various registers, program counters, and stack pointers
    • Stack space
    • errno variable
    • Signal Shielding Word
    • Scheduling priority

The line threading function we're going to learn is defined by the POSIX standard, called POSIX thread or pthread.
Line Program Control system
Creating Threads

The function prototype for creating the thread is as follows:

#include <pthread.h>
int pthread_create (pthread_t *thread, const pthread_attr_t *attr, void * (*start_routine ) (void *), void *arg);

Return value: Successfully returns 0, failure returns the error number.

After calling Pthread_create () in one thread to create a new thread, the current thread returns from Pthread_create () and continues to execute, while the code executed by the new thread passes us to the Pthread_create function pointer Start_ Routine decided. The Start_routine function receives an argument that is passed to it by the arg argument of Pthread_create, which is of type void*, which is defined by the caller himself by what type of interpretation. The return value type of the Start_routine is also void *, and the meaning of the pointer is also defined by the caller himself. When Start_routine returns, the thread exits, and other threads can invoke Pthread_join to get Start_routine's return value.

Once the pthread_create is successfully returned, the ID of the newly created thread is filled in to the memory unit that the thread parameter points to. We know that the type of process ID is pid_t, the ID of each process is unique throughout the system, the call Getpid can get the ID of the current process, is a positive integer value. The type of thread ID is thread_t, it is only guaranteed in the current process is unique, in different systems thread_t This type has a different implementation, it may be an integer value, may be a struct, or may be an address, so can not simply as an integer in printf print, Call Pthread_self to get the ID of the current thread.

Let's start by writing a simple example:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <pthread.h >
#include <unistd.h>

pthread_t ntid;

void Printids (const void *t)
{
    char *s = (char *) t;
  pid_t   pid;
  pthread_t Tid;

  PID = Getpid ();
  Tid = Pthread_self ();
  printf ("%s pid%u tid%u (0x%x) \ n", s, (unsigned int) PID,
      (unsigned int) tid, (unsigned int) tid)
;

void *thr_fn (void *arg)
{
  printids (ARG);
  return NULL;
}

int main (void)
{
  int err;

  Err = Pthread_create (&ntid, NULL, THR_FN, (void *) "Child Process:");
  if (Err!= 0) {
    fprintf (stderr, "can ' t create thread:%s\n", strerror (Err));
    Exit (1);
  }
  Printids ("Main thread:");
  Sleep (1);

  return 0;
}


Compilation execution results are as follows:

g++ thread.cpp-o thread-lpthread
./thread
main thread:pid 21046 tid 3612727104 (0xd755d740) child
Process: PID 21046 tid 3604444928 (0xd6d77700)

As you can tell from the result, the thread_t type is an address value, and multiple threads that belong to the same process call Getpid can get the same process number, and the thread numbers that the call pthread_self get are different.

If any one of the threads calls exit or _exit, all threads of the entire process terminate, and since return from the main function is equivalent to calling exit, we delay 1 seconds before the main function is closed, in order to stop the newly created thread from being executed. This is just a stopgap, even if the main thread wait 1 seconds, the kernel will not necessarily dispatch the newly created thread execution, next, we learn a better solution.
terminating a thread

If you need to terminate only a thread instead of terminating the entire process, there are three ways:

    1. Return from the thread function. This method does not adapt to the main thread, and return from the main function is equivalent to calling exit.
    2. One thread can invoke Pthread_cancel to terminate another thread in the same process.
    3. Threads can call pthread_exit terminate themselves.

This article mainly introduces the usage of Pthread_exit and pthread_join.

#include <pthread.h>

void pthread_exit (void *value_ptr);

Value_ptr is the void* type, as is the use of the return value of the thread function, other threads can invoke the Pthread_join to fetch the pointer.
Note that the memory unit that the Pthread_exit or return pointer points to must be global or allocated with malloc, and cannot be allocated on the stack of thread functions, because the thread function has exited when other threads get the return pointer.

#include <pthread.h>

int pthread_join (pthread_t thread, void **value_ptr);

Return value: Successfully returns 0, failure returns the error number.

The thread that calls the function suspends the wait until the thread with ID thread terminates. Thread threads terminate in different ways, and the termination status obtained by Pthread_join is different, summarized as follows:

    • When the thread thread returns through return, the cell that the value_ptr points to is the return value of the thread thread function.
    • If the thread thread is terminated by another thread calling the Pthread_cancel exception, the value_ptr point to the cell that holds the constant pthread_canceled.
    • If the thread thread is pthread_exit terminated by itself, the unit to which the value_ptr points is stored is the argument passed to Pthread_exit.

If you are not interested in the end state of the thread threads, you can pass NULL to the VALUE_PTR parameter. The reference code is as follows:

 #include <stdio.h> #include <stdlib.h> #include <pthread.h> #include <
  unistd.h> void* thread_function_1 (void *arg) {printf ("Thread 1 running\n");
return (void *) 1;
  } void* thread_function_2 (void *arg) {printf ("Thread 2 exiting\n");
Pthread_exit ((void *) 2);
    } void* thread_function_3 (void* Arg) {while (1) {printf ("Thread 3 writeing\n");
  Sleep (1);
  int main (void) {pthread_t tid;

  void *tret;
  Pthread_create (&tid, NULL, thread_function_1, NULL);
  Pthread_join (Tid, &tret);

  printf ("Thread 1 exit code%d\n", * (int*) (&tret));
  Pthread_create (&tid, NULL, thread_function_2, NULL);
  Pthread_join (Tid, &tret);

  printf ("Thread 2 exit code%d\n", * (int*) (&tret));
  Pthread_create (&tid, NULL, thread_function_3, NULL);
  Sleep (3);
  Pthread_cancel (TID);
  Pthread_join (Tid, &tret);

  printf ("Thread 3 exit code%d\n", * (int*) (&tret));
return 0; }

The results of the operation are:

Thread 1 running
thread 1 exit code 1
thread 2 exiting
thread 2 exit code 2
thread 3 writeing
thread 3 Writeing
thread 3 writeing
thread 3 exit code-1


Thus, the value of the constant pthread_canceled in the Linux pthread Library is 1. You can find its definition in the header file Pthread.h:

#define PTHREAD_CANCELED ((void *)-1)


Synchronization between Threads

Multiple threads may conflict when accessing shared data at the same time, for example, two threads will have to add a global variable 1, this operation on a platform requires three instructions to complete:

    • Reads variable values from memory to registers.
    • Register value plus 1.
    • Writes the value of the register back to memory.

This is an easy time to have two processes simultaneously manipulating register variable values, resulting in incorrect results.

The solution is to introduce mutex (mutex, Mutual Exclusive Lock), the thread that gets the lock can complete the read-modify-write operation, then release the lock to the other thread, the thread that does not get the lock can only wait and cannot access the shared data, so "read-modify-write" The three-step operation consists of an atomic operation, either executed or not executed, does not execute to the intermediate interrupt, nor does the operation in parallel on other processors.

A mutex is represented by a variable of type pthread_mutex_t, which can be initialized and destroyed as follows:

#include <pthread.h>

int pthread_mutex_destory (pthread_mutex_t *mutex);
int Pthread_mutex_int (pthread_mutex_t *mutex, const pthread_mutexattr_t *attr);
pthread_mutex_t mutex = Pthead_mutex_initializer;


Return value: Successfully returns 0, failure returns the error number.

A mutex initialized with the Pthread_mutex_init function can be destroyed with Pthread_mutex_destroy. If the MUTEX variable is statically allocated (global or static), it can also be initialized with a macro definition Pthread_mutex_initializer, which is equivalent to initializing with Pthread_mutex_init and attr parameters to null. The lock and unlock operations of a mutex can be done with the following functions:

#include <pthread.h>

int pthread_mutex_lock (pthread_mutex_t *mutex);
int Pthread_mutex_trylock (pthread_mutex_t *mutex);
int Pthread_mutex_unlock (pthread_mutex_t *mutex);


Return value: Successfully returns 0, failure returns the error number.

A thread can invoke Pthread_mutex_lock to obtain a mutex, and if another thread has already called Pthread_mutex_lock to obtain the mutex, the current thread needs to suspend the wait until another thread calls the Pthread_mutex_ Unlock releases the mutex, and the current thread is awakened in order to obtain the mutex and continue execution.

We use a mutex to solve the problem that two threads mentioned above can cause disturbances to global variable +1 at the same time:

#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>

#define NLOOP 5000

int counter;
pthread_mutex_t Counter_mutex = Pthread_mutex_initializer;

void *do_add_process (void *vptr)
{
  int i, Val;

  for (i = 0; i < Nloop i + +) {
    pthread_mutex_lock (&counter_mutex);
    val = counter;
    printf ("%x:%d\n", (unsigned int) pthread_self (), Val + 1);
    Counter = val + 1;
    Pthread_mutex_unlock (&counter_mutex);
  }

  return NULL;
}

int main ()
{
  pthread_t tida, tidb;

  Pthread_create (&tida, NULL, do_add_process, NULL);
  Pthread_create (&TIDB, NULL, do_add_process, NULL);

  Pthread_join (Tida, NULL);
  Pthread_join (TIDB, NULL);

  return 0;
}

In this way, each run can be displayed to 10000. If you remove the lock mechanism, there may be a problem. This mechanism is similar to the Java synchronized block mechanism.
Condition Variable

Synchronization between threads there is also a situation where thread a needs to wait for a certain condition to continue, and now that the condition is not established, thread a blocks the wait, and thread B causes the condition to be created during execution, and wakes thread A to continue executing. In the Pthread library, a conditional variable (conditiion Variable) is used to block waiting for a condition, or to wake up a thread that waits for this condition. Condition variable are represented by variables of type pthread_cond_t, which can be initialized and destroyed as follows:

#include <pthread.h>

int pthread_cond_destory (pthread_cond_t *cond);
int Pthread_cond_init (pthead_cond_t *cond, const pthread_condattr_t *attr);
pthread_cond_t cond = Pthread_cond_initializer;

Return value: Successfully returns 0, failure returns the error number.

Similar to the initialization and destruction of a mutex, the Pthread_cond_init function initializes a condition variable,attr argument to NULL to represent the default property, Pthread_cond_ The destroy function destroys a condition Variable. If the condition variable is statically assigned, it can also be initialized with a macro definition Pthead_cond_initializer equivalent to initializing with the Pthread_cond_init function and the attr parameter to NULL. Condition variable operations can be done with the following functions:

#include <pthread.h>

int pthread_cond_timedwait (pthread_cond_t *cond, pthread_mutex_t *mutex, const struct Timespec *abstime);
int pthread_cond_wait (pthread_cond_t *cond, pthread_mutex_t *mutex);
int Pthread_cond_broadcast (pthread_cond_t *cond);
int pthread_cond_signal (pthread_cond_t *cond);


Visible, a condition variable is always used with a mutex. A thread can invoke pthread_cond_wait to block the wait on a condition variable, this function does the following three steps:

    1. Releases the mutex.
    2. Blocking wait.
    3. When awakened, regain the mutex and return.

The Pthread_cond_timedwait function also has an additional parameter to set the wait timeout and returns etimedout if no other thread wakes the current thread at the time specified by Abstime. One thread can invoke pthread_cond_signal to wake up another thread waiting on a condition variable, or call Pthread_cond_broadcast to wake up in this condition All threads waiting on the variable.

The following program illustrates a producer-consumer example where the producer produces a structure string on the table head of the list, and the consumer takes the structural body from the table head.

#include <stdio.h> #include <stdlib.h> #include <pthread.h> #include <unistd.h> struct MSG {s
  Truct msg *next;
int num;

};
struct MSG *head;
pthread_cond_t has_product = Pthread_cond_initializer;

pthread_mutex_t lock = Pthread_mutex_initializer;

  void* consumer (void *p) {struct MSG *mp; for (;;)
    {Pthread_mutex_lock (&lock);
    while (head = = NULL) {pthread_cond_wait (&has_product, &lock);
    MP = head;
    Head = mp->next;
    Pthread_mutex_unlock (&lock);
    printf ("Consume%d\n", mp->num);
    Free (MP);
  Sleep (rand ()% 5);

  } void* producer (void *p) {struct MSG *mp; for (;;)
    {MP = (struct msg *) malloc (sizeof (*MP));
    Pthread_mutex_lock (&lock);
    Mp->next = head;
    Mp->num = rand ()% 1000;
    Head = MP;
    printf ("Product%d\n", mp->num);
    Pthread_mutex_unlock (&lock);
    Pthread_cond_signal (&has_product);
  Sleep (rand ()% 5); int main () {pthread_t PID, CID;

  Srand (Time (NULL));
  Pthread_create (&pid, NULL, producer, NULL);

  Pthread_create (&cid, NULL, consumer, NULL);
  Pthread_join (PID, NULL);

  Pthread_join (CID, NULL);
return 0;

 }

Related Article

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.