Objective
Thread? Why do we need threads when we have processes, and what are their differences? What are the advantages of using threads? There are also some details of multithreaded programming, such as how threads are synchronized and mutually exclusive, which are described in this article. I saw such an interview question in a 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 mainline, print "This is Thread1" and set G_flag to 1
3 Start thread 2 in the mainline, print "This is Thread2" and set G_flag to 2
4) Line Program 1 requires a thread 2 exit before exiting
5 The main thread exits when it detects g_flag from 1 to 2, or from 2 to 1
We start this article with this question, and when we're done, everyone will do it. The framework of this article is as follows:
1, process and thread
A process is an instance of the execution of a program, that is, the collection of data structures to which the program has been executed. From the kernel point of view, the purpose of the process is to serve as the basic unit of allocating system resources (CPU time, memory, etc.).
A thread is a stream of execution of a process, the basic unit of CPU scheduling and dispatch, which is a smaller unit that can operate independently than a process. A process consists of several threads (the user program that has many relatively independent execution streams shares most of the data structures of the application), and the thread shares all the resources owned by the process with other threads of the same process.
"Process-the smallest unit of resource allocation, thread-the smallest unit of program execution"
Processes have separate address spaces, and when a process crashes, it does not affect other processes in protected mode, and threads are just different execution paths 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 dead, so the process of multiple processes than multithreaded program is robust, but in the process of switching, the resource consumption is greater, the efficiency is some worse. However, for concurrent operations that require simultaneous and share certain variables, only threads can be used, and processes cannot be used.
2, the use of thread reasons
From the above we know the difference between process and thread, in fact, these differences are the reasons why we use threads. In general, the process has a separate address space, and the thread does not have a separate address space (the thread in the same process is sharing the address space of the process). (The following is excerpted from multithreaded programming under Linux)
One reason to use multithreading is that it is a very "frugal" multitasking operation compared to a process. We know that under a Linux system, starting a new process must be assigned to its own address space, a large number of data tables to maintain its code snippets, stack segments and data segments, which is an "expensive" multitasking way of working. While running on multiple threads in a process, they use the same address space, share most of the data, and start a thread much less space than 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, overall, the cost of a process is about 30 times times the cost of a thread, of course, on the specific system, this data may be a big difference.
The second reason to use multithreading is the convenient communication mechanism between threads. For different processes, they have a separate data space, the transmission of data can only be carried out by means of communication, which is not only time-consuming, but also inconvenient. Threads Otherwise, because the data space is shared between threads under the same process, the data for one thread can be used directly for other threads, which is quick and 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 subroutines declared as static data is more likely to bring a catastrophic attack on multithreaded programs, which is the most need to note when writing multithreaded program.
In addition to the above mentioned advantages, not compared with the process, multithreaded procedure as a multitasking, concurrent work, of course, has the following advantages:
- Increase application responsiveness. This is especially useful for graphical interface programs, when an operation takes a long time, the whole system will wait for this operation, when the program does not respond to keyboard, mouse, menu operation, and using multithreading technology, the time-consuming operation (time consuming) on a new thread, you can avoid this embarrassing situation.
- Make multiple 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 structure of the program. A long and complex process can be considered into multiple threads to become several independent or half independent parts of the operation, such programs will facilitate understanding and modification.
=============================
From a function call, the process creates using the fork () action, and the thread creates a clone () action. This is what Richard Stevens said:
Fork is expensive. Memory is copied from the parent to the "child", all descriptors are duplicated in the "Child", and "." Current implementations use a technique called copy-on-write, which avoids a copy of the parent ' s data spaces to the child Until the child needs its own copy. But, regardless of this optimization, fork is expensive.
The IPC is required to pass information between, the parent and child, after the fork. Passing information from the ' parent to the ' child before the fork are easy, since the child starts with a copy of the parent ' s data spaces and with a copy of the ' descriptors '. But, returning information from the "child" to the parent takes more work.
Threads help with both problems. Threads are sometimes called lightweight processes since a thread is "lighter weight" than a process. The thread creation can be 10–100 The 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 *), voi D *arg);
int Pthread_join (pthread_t tid, void * * status);
pthread_t pthread_self (void);
int Pthread_detach (pthread_t tid);
void Pthread_exit (void *status);
Pthread_create is used to create a thread that returns 0 successfully or returns EXXX (positive number).
- pthread_t *tid: The thread ID type is pthread_t, usually an unsigned integer, which is returned through the *tid pointer when the call Pthread_create succeeds.
- Const pthread_attr_t *ATTR: Specifies the properties of the thread to be created, such as thread priority, initial stack size, daemon, and so on. You can use NULL to use default values, and usually we use default values.
- void * (*FUNC) (void *): function pointer func, which specifies the function to execute when a 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.
Pthread_join is used to wait for a thread to exit, return 0 successfully, or return exxx (positive number).
- pthread_t tid: Specifies the thread ID to wait on
- void * Status: If not NULL, then the return value of the thread is stored in the space that the status points to (this is why the status is a two-level pointer!). This parameter is also known as the value-result argument.
Pthread_self is used to return the ID of the current thread.
Pthread_detach is used to specify that a thread becomes detached, just as a process becomes detached from the terminal and is similar to a background process. 0 is returned successfully, otherwise exxx (positive number) is returned. A thread that becomes a detached state, and all its resources are freed if the thread exits. If it is not a detached state, the thread must retain its thread ID, exiting until the other thread calls it pthread_join.
The process is similar, which is why when we open the Process Manager, we find that there are a lot of zombie processes! 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.
Having known these functions, we tried to do the first thing in this article:
1) There is an int type global variable G_flag initial value is 0;
2 start thread 1 in mainline, print "This is Thread1" and set G_flag to 1
3 Start thread 2 in the mainline, print "This is Thread2" and set G_flag to 2
These 3 points are very simple!!! It's not just calling Pthread_create to create a thread. The code is as follows:
* * 1) there is an int type global variable G_flag initial value is 0; * * 2) starting thread 1 in the mainline, printing "This is Thread1" and setting G_flag to 1 * * 3) starting thread 2 in the mainline, printing "This is Thread2" , and set G_flag 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 is started, a single thread is created, called the initial thread or main thread.
* Additional threads are created by Pthread_create.
* So we just need to create two thread in main ().
*/int main (int argc, char** argv) {printf ("Enter main\n");
pthread_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, Thread1, &TID2);
if (RC1!= 0) printf ("%s:%d\n", __func__, Strerror (RC1));
printf ("Leave main\n");
Exit (0); } * * THREAD1 () would be execute by Thread1, after Pthread_create () * It'll set g_flag = 1;
* * void* thread1 (void* Arg) {printf ("Enter thread1\n");
printf ("This is Thread1, G_flag:%d, thread ID is%u\n", G_flag, (unsigned int) pthread_self ());
G_flag = 1;
printf ("This is Thread1, G_flag:%d, thread ID is%u\n", G_flag, (unsigned int) pthread_self ());
printf ("Leave thread1\n");
Pthread_exit (0);
/* * THREAD2 () would be execute by Thread2, after Pthread_create () * It'll set g_flag = 2;
* * void* thread2 (void* Arg) {printf ("Enter thread2\n");
printf ("This is Thread2, G_flag:%d, thread ID is%u\n", G_flag, (unsigned int) pthread_self ());
G_flag = 2;
printf ("This is Thread1, G_flag:%d, thread ID is%u\n", G_flag, (unsigned int) pthread_self ());
printf ("Leave thread2\n");
Pthread_exit (0);
}
This completes the three-point requirement of 1, 2, 3). The following results were compiled:
If the function in the Pthread library is used in the program, in addition to the #include<pthread.h>, the-lpthread option is added at compile time.
netsky@ubuntu:~/workspace/pthead_test$./a.out
Enter main
Enter thread2 this
is thread2, g_flag:0, thread ID is 3079588720 This is
thread1, g_flag:2, thread ID is 3079588720
leave thread2 leave
main
Enter thread 1 This
is Thread1, g_flag:2, thread ID are 3071196016 This is
thread1, g_flag:1, thread ID is 3071196016
But the results are not necessarily the above, and there may be:
netsky@ubuntu:~/workspace/pthead_test$./a.out
Enter main
leave main
Enter Thread1
this is Thread1, g_flag:0, thread ID is 3069176688 This
are thread1, g_flag:1, thread ID is 3069176688
Or is:
netsky@ubuntu:~/workspace/pthead_test$./a.out
Enter Main
Wait a minute. This is also well understood because it depends on when the main thread main function terminates, and whether the thread thread1, thread2 or not, can rush to execute their functions. This is also a problem to be aware of when multithreaded programming, because it is possible for a thread to affect all other threads throughout the process! If we sleep () for a period of time before the main function exits, we can ensure that thread1 and thread2 are ready to execute.
Attention: We must have noticed that Pthread_exit was called before the thread function Thread1 (), Thread2 () was executed. 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 returned, and only the thread function returns, and the thread exits.
- Exit is a process exit, and if exit is called in the thread function, all functions in the process will exit!
"4" line program 1 need a thread 2 exit to exit "4th also very easy to solve, directly in the THREAD1 function before the call Pthread_join on OK."
4, the mutual exclusion between threads
The above code seems to be a good solution to the problem of the previous 4 requirements, is NOT!!! Because G_flag is a global variable, thread thread1 and thread2 can operate on it at the same time, requiring it to be locked for protection, thread1 and thread2 to be mutually exclusive access. Here's how to lock protection-mutexes.
Mutual exclusion Locks:
Using mutexes (mutexes) enables threads to execute sequentially. In general, mutexes synchronize multiple threads by ensuring that only one thread executes a critical section of code at a time. Mutexes can also protect single-threaded code.
The related operation functions of the mutex are 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 unlock. And before that, you need to declare a variable of type pthread_mutex_t, which is used as a parameter to the previous two functions. See section 5th for specific code.
5, synchronization between the threads
5th-The main thread exits when it detects g_flag from 1 to 2, or from 2 to 1. You need to use thread synchronization technology! A condition variable is required for thread synchronization.
Condition variable:
You can use conditional variables to block threads in an atomic way until a specific condition is true. A condition variable is always used in conjunction with a mutex lock. The test of the condition is performed under the protection of a mutex (mutex).
If the condition is false, the thread usually blocks based on the condition variable and releases the mutually exclusive lock that waits for the condition to change in atomic way. If another thread changes the condition, the thread may signal to the associated condition variable, which causes one or more waiting threads to do the following:
- Awaken
- Get 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 is shared by the collaboration process
"Use conditional variables to block threads in an atomic manner until a specific condition is true." To use the 5th, the main thread main function is blocked waiting for G_flag to change from 1 to 2, or from 2 to 1. The related functions of the condition variable are 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 a blocked thread that a particular condition is true. You need to declare a variable of the pthread_cond_t type before the caller two function for the parameters of the two functions.
Why is a conditional variable always used with a mutex, and is the test of the condition performed under the protection of a mutex (mutex)? Because "an 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 simply wakes up a thread that waits for a condition variable. If you need to wake up all 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);
If the time is up, the condition variable is not true, still returns, and the return value is etime.
6. Final Code of test questions
From the previous introduction, we can easily write out the code, as follows:
* 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 of 0; 2 in the mainline call start thread 1, print "This is Thread1", and set the G_flag to 1 3, starting thread 2 in the mainline, printing "This is Thread2 ", and set G_flag to 2 4) Line program 1 requires a thread 2 exit before exiting 5 The main thread exits when it detects g_flag 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 is started, a single thread is created, called the initial thread or main thread.
* Additional threads are created by Pthread_create.
* So we just need to create two thread in main ().
*/int main (int argc, char** argv) {printf ("Enter main\n");
pthread_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, Thread1, &TID2);
if (RC1!= 0) printf ("%s:%d\n", __func__, Strerror (RC1));
Pthread_cond_wait (&cond, &mutex);
printf ("Leave main\n");
Exit (0);
/* * THREAD1 () would be execute by Thread1, after Pthread_create () * It'll set g_flag = 1;
* * void* thread1 (void* Arg) {printf ("Enter thread1\n");
printf ("This is Thread1, G_flag:%d, thread ID is%u\n", G_flag, (unsigned int) pthread_self ());
Pthread_mutex_lock (&mutex);
if (G_flag = = 2) pthread_cond_signal (&cond);
G_flag = 1;
printf ("This is Thread1, G_flag:%d, thread ID is%u\n", G_flag, (unsigned int) pthread_self ());
Pthread_mutex_unlock (&mutex);
Pthread_join (* (pthread_t*) arg, NULL);
printf ("Leave thread1\n");
Pthread_exit (0);
/* * THREAD2 () would be execute by Thread2, after Pthread_create () * It'll set g_flag = 2;
* * void* thread2 (void* Arg) {printf ("Enter thread2\n");
printf ("This is Thread2, G_flag:%d, thread ID is%u\n", G_flag, (unsigned int) pthread_self ()); Pthread_muteX_lock (&mutex);
if (G_flag = = 1) pthread_cond_signal (&cond);
G_flag = 2;
printf ("This is Thread2, G_flag:%d, thread ID is%u\n", G_flag, (unsigned int) pthread_self ());
Pthread_mutex_unlock (&mutex);
printf ("Leave thread2\n");
Pthread_exit (0);
}
Compile to run can get the result that meets the requirement!
The above is the entire content of this article, I hope to help you learn, but also hope that we support the cloud habitat community.