Linux multithreaded Programming (unlimited Linux)

Source: Internet
Author: User
Tags mutex posix

Objective

Thread? Why are there processes that require threads, and what is the difference between them? What are the advantages of using threads? There are also some details about multi-threading programming, such as how threads are synchronized and mutually exclusive, and these things are described in this article. I see such an interview problem in some QQ group:

Are you familiar with POSIX multithreaded programming technology? If familiar, write the program to complete the following functions:

1) There is an int type global variable G_flag initial value is 0;

2) Start thread 1 in the mainline call, print "This is Thread1" and set G_flag to 1

3) Start thread 2 in the mainline call, print "This is Thread2" and set G_flag to 2

4) Line program 1 requires thread 2 exit before exiting

5) The main thread exits when it detects that G_flag has changed from 1 to 2, or from 2 to 1

We started this article with this question, and after that, everyone would do it. The framework of this article is as follows:

    • 1. Process and Thread
    • 2. Reasons for using threads
    • 3. Functions related to threading operations
    • 4. Mutual exclusion between threads
    • 5. Synchronization between threads
    • 6. Final Test Code
1. Process and Thread

A process is an instance of the execution of a program, which is the collection of data structures to what extent the program has performed. From the kernel point of view, the purpose of the process is to assume the basic unit of allocating system resources (CPU time, memory, etc.).

A thread is an execution flow of a process that is the basic unit of CPU dispatch and dispatch, which is a smaller unit that can run independently than a process. A process consists of several threads (a user program with many relatively independent execution flows that shares most of the data structures of the application), and the threads share all the resources owned by the process with other threads that belong to one process.

"Process-the smallest unit of resource allocation, thread-the smallest unit of program execution"

The process has a separate address space, and after a process crashes, it does not affect other processes in protected mode, and the thread is just a different execution path in a process. Thread has its own stack and local variables, but the thread does not have a separate address space, a thread dead is equal to the entire process to die, so the multi-process program is more robust than multi-threaded programs, but in the process of switching, the cost of large resources, efficiency is worse. But for some concurrent operations that require simultaneous and shared variables, only threads can be used, and processes cannot be used.

2. Reasons for using threads

From the above we know the difference between the process and the thread, in fact, these differences are the reason why we use threads. In general, the process has a separate address space, and the thread does not have a separate address space (the address space of the thread sharing process within the same process). (The following is an excerpt from multithreaded programming under Linux)

One of the reasons to use multithreading is that it is a very "frugal" multi-tasking approach compared to the process. We know that under the Linux system, starting a new process must be assigned to its own address space, creating numerous data tables to maintain its code snippets, stack segments, and data segments, which is an "expensive" multi-tasking way of working. While running on multiple threads in a process that use the same address space, sharing most of the data, starting a thread is much less than the space it takes to start a process, and the time it takes to switch between threads is much less than the time it takes to switch between processes. According to statistics, in general, the cost of a process is about 30 times times the cost of a thread, of course, on a specific system, this data may be significantly different.

The second reason to use multithreading is the convenient communication mechanism between threads. For different processes, they have independent data space, it is not only time-consuming, but also inconvenient to transmit the data only by means of communication. Threads do not, because data space is shared between threads in the same process, so that the data of one thread can be used directly by other threads, which is not only fast, but also convenient. Of course, the sharing of data also brings some other problems, some variables can not be modified by two threads at the same time, some of the sub-programs declared as static data more likely to have a catastrophic attack on the multi-threaded program, these are the most important to write a multi-thread programming.

In addition to the advantages mentioned above, not compared with the process, multi-threaded procedure as a multi-tasking, concurrent work, of course, the following advantages:

    • Improve application responsiveness. This is especially meaningful to the graphical interface program, when an operation takes a long time, the entire system waits for this operation, the program does not respond to the keyboard, mouse, menu operation, and the use of multi-threading technology, the time-consuming operation (consuming) into a new thread, can avoid this embarrassing situation.
    • Make multi-CPU systems more efficient. The operating system guarantees that when the number of threads is not greater than the number of CPUs, different threads run on different CPUs.
    • Improve the program structure. A long and complex process can be considered to be divided into multiple threads and become a separate or semi-independent part of the run, which facilitates understanding and modification.

=============================

From the function call, process creation uses the fork () operation, and thread creation uses the Clone () operation. Master Richard Stevens said:

    • fork  is expensive. Memory is copied from the parent to the child, all descriptors be duplicated in the child, and so on. Current implementations use a technique called copy-on-write which avoids a copy of the parent's data space to the child Until the child needs its own copy. But, regardless of this optimization,  fork  is expensive.

    • IPC is required-pass information between the parent and child after the  Fork . Passing information from the parent to the child before the  Fork  is Easy, since the child starts with a Copy of the parent's data space and with a copy of the the parent ' s descriptors. But, returning information from the child to the parent takes more work.

Threads help with both problems. Threads is sometimes called lightweight processes since a thread is "lighter weight" than a process. That's, thread creation can be 10–100 times faster than process creation.

All threads within a process share the same global memory. This makes the sharing of information easy between the threads, but along with this simplicity comes the problem of SYNCHR Onization.

=============================

3. Functions related to threading operations
#include <pthread.h> int pthread_create (pthread_t *tid, const pthread_attr_t *attr, void * (*FUNC) (void *), void *ar  g) int Pthread_join (pthread_t tid, void * * status);p thread_t pthread_self (void); int Pthread_detach (pthread_t tid); void Pthread_exit (void *status);

Pthread_create is used to create a thread that successfully returns 0, otherwise returns EXXX (a positive number).

    • pthread_t *tid: The type of thread ID is pthread_t, usually an unsigned integer, returned by a *tid pointer when the call Pthread_create succeeds.
    • Const pthread_attr_t *ATTR: Specifies the properties of the creation thread, such as the thread priority, the initial stack size, whether it is a daemon, and so on. The default value can be used with NULL, which is usually the case with default values.
    • void * (*FUNC) (void *): function pointer func, which specifies the function to be executed after the new thread is created.
    • void *arg: The parameters of the function that the thread will execute. If you want to pass multiple parameters, encapsulate them in a struct.

The pthread_join is used to wait for a thread to exit, return 0 successfully, or return exxx (a positive number).

    • pthread_t tid: Specifies the thread ID to wait for
    • void * Status: If it is not NULL, then the return value of the thread is stored in the space where status is pointing (that is why status is a level two pointer!). This is also known as the "value-result" parameter.

The pthread_self is used to return the ID of the current thread.

Pthread_detach is used to specify that the thread becomes detached , as if the process were out of terminal and turned into a background process. Successful return 0, otherwise return exxx (positive). A thread that becomes detached, and if the thread exits, all its resources are freed. If it is not a detached state, the thread must retain its thread ID, exiting the state until other threads call it pthread_join.

Process is similar, this is also when we open the process manager, found that there are many zombie process reasons! is also why must have zombie this process state.

Pthread_exit is used to terminate a thread, you can specify a return value so that other threads get the return value of the thread through the Pthread_join function.

    • void *status: Refers to the return value of the thread termination.

Once we know these functions, we try to complete the first issue of this article:

1) There is an int type global variable G_flag initial value is 0;

2) Start thread 1 in the mainline call, print "This is Thread1" and set G_flag to 1

3) Start thread 2 in the mainline call, print "This is Thread2" and set G_flag to 2

These 3 points are very simple!!! Not just call pthread_create to create the thread. The code is as follows:

/* * 1) there is an int type global variable G_flag initial value of 0; * 2) starting thread 1 in the main line, printing "This is Thread1" and setting the G_flag to 1 * * 3) starting thread 2 in the mainline call, printing "This is Thread2" and G_f Lag set to 2 * */#include <stdio.h> #include <stdlib.h> #include <pthread.h> #include <errno.h># Include<unistd.h>int g_flag=0;void* Thread1 (void*); void* thread2 (void*);/* When program was started, a single thre Ad is created, called the initial thread or main thread. * Additional threads is created by Pthread_create. * So we just need to create both thread in main (). */int Main (int argc, char** argv) {printf ("Enter main\n");p thread_t tid1, Tid2;int rc1=0, Rc2=0;rc2 = Pthread_create (& Tid2, NULL, THREAD2, NULL), if (RC2! = 0) printf ("%s:%d\n", __func__, Strerror (RC2)); RC1 = Pthread_create (&tid1, NULL, t Hread1, &AMP;TID2); if (RC1! = 0) printf ("%s:%d\n", __func__, Strerror (RC1));p rintf ("Leave main\n"); exit (0);} /* * THREAD1 () would be the execute by Thread1, after Pthread_create () * It would set g_flag = 1; */void* thread1 (void* Arg) {printf ("Enter thread1\n");p rintf (" This was Thread1, G_flag:%d, thread ID is%u\n ", G_flag, (unsigned int) pthread_self ()); G_flag = 1;printf (" This I S thread1, G_flag:%d, thread ID is%u\n ", G_flag, (unsigned int) pthread_self ());p rintf (" Leave thread1\n ");p thread_exit (0 );} /* * THREAD2 () would be the execute by Thread2, after Pthread_create () * It would set G_flag = 2; */void* thread2 (void* Arg) {printf ("Enter thread2\n");p rintf ("This was thread2, G_flag:%d, thread ID is%u\n", G_flag, (Unsi gned int) pthread_self ()) G_flag = 2;printf ("This was Thread1, G_flag:%d, thread ID is%u\n", G_flag, (unsigned int) pthread_ Self ());p rintf ("Leave thread2\n");p thread_exit (0);}

This completes the 1), 2), 3) of these three-point requirements. The compilation performs the following results:

[Email protected]:~/workspace/pthead_test$ gcc-lpthread test.c

If a function in the Pthread library is used in the program, in addition to #include<pthread.h>, there is a-lpthread option at compile time.
[Email protected]:~/workspace/pthead_test$./a.out
Enter Main
Enter Thread2
This was thread2, g_flag:0, thread ID is 3079588720
This was thread1, G_FLAG:2, thread ID is 3079588720
Leave Thread2
Leave main
Enter Thread1
This was thread1, G_FLAG:2, thread ID is 3071196016
This was thread1, g_flag:1, thread ID is 3071196016
Leave Thread1
But the result is not necessarily the above, and it may be:

[Email protected]:~/workspace/pthead_test$./a.out
Enter Main
Leave main
Enter Thread1
This was thread1, g_flag:0, thread ID is 3069176688
This was thread1, g_flag:1, thread ID is 3069176688
Leave Thread1

Or is:

[Email protected]:~/workspace/pthead_test$./a.out
Enter Main
Leave main
Wait a minute. This is also very well understood because, depending on when the main thread main function terminates, the thread thread1, thread2 whether the function can be rushed to execute their functions. This is also an issue to be aware of when multithreaded programming, because it is possible that one thread will affect all other threads in the entire process! If we go to sleep () for a period of time before the main function exits, we can guarantee that Thread1 and thread2 will be able to execute.

Attention: It must have been noticed that we called pthread_exit before the thread function Thread1 (), Thread2 () had finished executing. What if I call exit () or return? Try it yourself!

Pthread_exit () is used for thread exit, and you can specify a return value so that other threads get the return value of the thread through the Pthread_join () function.
Return is the function return, only the thread function return, the thread will exit.
Exit is the process exit, and if exit is called in the thread function, all functions in the process will exit!

"4) Line program 1 requires thread 2 exit before exiting" 4th is also easy to solve, directly before the THREAD1 function exit call Pthread_join on OK.

4. Mutual exclusion between threads

The above code seems to be a good solution to the problem of the front 4 point requirements, not really!!! Because G_flag is a global variable, thread thread1 and thread2 can operate on it at the same time, requiring it to be lock-protected and Thread1 and thread2 to be mutually exclusive. Here we describe how to lock protection-mutex.

Mutual exclusion Lock:

The use of mutexes (mutexes) enables threads to execute sequentially. Typically, mutexes synchronize multiple threads by ensuring that only one thread executes the critical segment of code at a time. Mutexes can also protect single-threaded code.

The associated operation function for the mutex is as follows:

#include <pthread.h> int Pthread_mutex_lock (pthread_mutex_t * mptr); int Pthread_mutex_unlock (pthread_mutex_t * mptr); Both return:0 if OK, positive exxx value On Error

Before the operation of the critical resources need to Pthread_mutex_lock first lock, after the operation Pthread_mutex_unlock again unlocked. And before that, you need to declare a variable of type pthread_mutex_t, which is used as an argument for the previous two functions. See section 5th for specific codes.

5. Synchronization between threads

5th-The main thread exits when it detects that G_flag has changed from 1 to 2, or from 2 to 1. You need to use thread synchronization technology! A conditional variable is required for thread synchronization.

Condition variables:

You can use conditional variables to block threads atomically until a particular condition is true. A condition variable is always used with a mutex. The test of the condition is performed under the protection of the mutex (mutex).

If the condition is false, the thread usually blocks based on the condition variable and atomically releases the mutex that waits for the condition to change. If another thread changes the condition, the thread may signal the related condition variable, allowing one or more waiting threads to do the following:

  • Awaken
  • Get the mutex again
  • Re-evaluate conditions

A condition variable can be used to synchronize threads between processes in the following situations:

  • Threads are allocated in memory that can be written to
  • Memory shared by the collaboration process

Use a condition variable to block a thread atomically until a particular condition is true. "Can be used to 5th, the main thread main function block waiting for G_flag from 1 to 2, or from 2 to 1. The relevant function of the condition variable is as follows:

#include <pthread.h> int pthread_cond_wait (pthread_cond_t *cptr, pthread_mutex_t *mptr); int pthread_cond_signal (pthread_cond_t *cptr); Both return:0 if OK, positive exxx value On Error

Pthread_cond_wait is used to wait for a particular condition to be true, Pthread_cond_signal is used to notify the blocked thread that a particular condition is true. Before the caller two functions, it is necessary to declare a variable of type pthread_cond_t, which is used for the arguments of these two functions.

Why is a condition variable always used with a mutex, and the test of the condition is done under the protection of the mutex (mutex)? Because "one attribute condition" is usually a variable that is shared among multiple threads. mutexes allow this variable to be set and detected in different threads.

Typically, pthread_cond_wait just wakes up a thread that waits for a condition variable. If you need to wake up all the threads waiting for a condition variable, you need to call:

int Pthread_cond_broadcast (pthread_cond_t * cptr);

By default, blocked threads wait until they know that a condition variable is true. If you want to set the maximum blocking time, you can call:

int pthread_cond_timedwait (pthread_cond_t * cptr, pthread_mutex_t *mptr, const struct TIMESPEC *abstime);

If the time is up, the condition variable is still not true, and the return value is etime.

6. Final Test Code

From the previous introduction, we can easily write out the code, as follows:

/* Are you familiar with POSIX multithreaded programming technology? As familiar, write the program to complete the following functions: 1) There is an int type global variable G_flag initial value of 0, 2) in the main line called the start thread 1, print "This is Thread1", and set the G_flag to 1 3) in the main line called Thread 2, print "This is Thread2 ", and set G_flag to 2 4) line program 1 requires thread 2 to exit before exiting 5) The main thread exits when it detects that G_flag has changed from 1 to 2, or from 2 to 1 */#include <stdio.h> #include < stdlib.h> #include <pthread.h> #include <errno.h> #include <unistd.h>typedef void* (*fun) (void*); int g_flag=0;static pthread_mutex_t Mutex = pthread_mutex_initializer;static pthread_cond_t cond = PTHREAD_COND_ initializer;void* Thread1 (void*); void* thread2 (void*);/* When program was started, a single thread is created, called th E initial thread or main thread. * Additional threads is created by Pthread_create. * So we just need to create both thread in main (). */int Main (int argc, char** argv) {printf ("Enter main\n");p thread_t tid1, Tid2;int rc1=0, Rc2=0;rc2 = Pthread_create (& Tid2, NULL, THREAD2, NULL), if (RC2! = 0) printf ("%s:%d\n", __func__, Strerror (RC2)); RC1 = Pthread_create (&tid1, NULL, t Hread1, &AMP;TID2); if (RC1! = 0) printf ("%s:%d\n", __func__, Strerror (RC1));p thread_cond_wait (&cond, &mutex);p rintf ("Leave main\n"); Exit (0);} /* * THREAD1 () would be the execute by Thread1, after Pthread_create () * It would set g_flag = 1; */void* thread1 (void* Arg) {printf ("Enter thread1\n");p rintf ("This was Thread1, G_flag:%d, thread ID is%u\n", G_flag, (Unsi gned int) pthread_self ());p Thread_mutex_lock (&mutex), if (G_flag = = 2) pthread_cond_signal (&cond); g_Flag = 1; printf ("This was Thread1, G_flag:%d, thread ID is%u\n", G_flag, (unsigned int) pthread_self ());p Thread_mutex_unlock (& Mutex);p thread_join (* (pthread_t*) arg, NULL);p rintf ("Leave thread1\n");p thread_exit (0);} /* * THREAD2 () would be the execute by Thread2, after Pthread_create () * It would set G_flag = 2; */void* thread2 (void* Arg) {printf ("Enter thread2\n");p rintf ("This was thread2, G_flag:%d, thread ID is%u\n", G_flag, (Unsi gned int) pthread_self ());p Thread_mutex_lock (&mutex), if (G_flag = = 1) pthread_cond_signal (&cond); g_Flag = 2; printf ("This isThread2, G_flag:%d, thread ID is%u\n ", G_flag, (unsigned int) pthread_self ());p Thread_mutex_unlock (&mutex);p rintf ( "Leave thread2\n");p thread_exit (0);}

Compile and run to get the results that meet the requirements!

Linux multithreaded Programming (unlimited Linux)

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.