<pthread.h> of Linux

Source: Internet
Author: User
Tags mutex semaphore

Transferred from: http://blog.sina.com.cn/s/blog_66cc44d00100in5b.html

Multithreading under a Linux system follows the POSIX threading interface, called Pthread. To write multithreaded programs under Linux, you need to use the header file pthread.h, you need to use the library libpthread.a when connecting. by the way, the implementation of Pthread under Linux is implemented through the system call Clone (). Clone () is a Linux-specific system call that is used in a manner similar to fork, with details about clone () that interested readers can look at for documentation instructions. Below we show one of the simplest multi-threaded example1.c.

#include <stdio.h>

#include <pthread.h>
void thread (void)

{

int i;

for (i=0;i<3;i++)

printf ("This is a pthread.\n");

}

int main (void)

{

pthread_t ID;

int I,ret;

Ret=pthread_create (&id,null, (void *) thread,null);

if (ret!=0) {

printf ("Create pthread error!\n");

Exit (1);

}

for (i=0;i<3;i++)

printf ("This is the main process.\n");

Pthread_join (Id,null);

return (0);

}

We compile this program:

GCC example1.c-lpthread-o example1

Running example1, we get the following results:

This is the main process.

This is a pthread.

This is the main process.

This is the main process.

This is a pthread.

This is a pthread.

Run again, we may get the following result:

This is a pthread.

This is the main process.

This is a pthread.

This is the main process.

This is a pthread.
This is the main process

The results are different for two times, which is the result of two threads competing for CPU resources. In the example above, we used two functions, pthread_create and Pthread_join, and declared a variable of type pthread_t.

pthread_t is defined in the header file/usr/include/bits/pthreadtypes.h:

typedef unsigned long int pthread_t;

It is the identifier of a thread. The function pthread_create is used to create a thread whose prototype is:

extern int pthread_create __p ((pthread_t *__thread, __const pthread_attr_t *__attr,

void * (*__start_routine) (void *), void *__arg));

The first argument is a pointer to the thread identifier, the second parameter sets the Thread property, the third argument is the starting address of the thread's running function, and the last parameter is the argument that runs the function. Here, our function thread does not require arguments, so the last parameter is set to a null pointer. The second parameter we also set as a null pointer, which will generate the default properties of the thread. The setting and modification of thread properties is described in the next section. When creating a line Cheng, the function returns 0, if not 0, the creation thread fails, and the common error return code is eagain and einval. The former indicates that the system restricts the creation of new threads, such as excessive number of threads, which indicates that the second parameter represents an illegal thread property value. After the thread is created successfully, the newly created thread runs the function identified by parameter three and parameter four, and the original thread continues to run the next line of code.

The function pthread_join is used to wait for the end of a thread. The function prototypes are:

extern int Pthread_join __p ((pthread_t __th, void **__thread_return));

The first parameter is the waiting thread identifier, and the second parameter is a user-defined pointer that can be used to store the return value of the waiting thread. This function is a thread-blocking function, and the function that invokes it waits until the thread that is waiting ends, and when the function returns, the resource that is waiting for the thread is retracted. There are two ways to end a thread, one is like our example above, the function ends, the thread that calls it ends, and the other is implemented by the function Pthread_exit. Its function prototypes are:

extern void Pthread_exit __p ((void *__retval)) __attribute__ ((__noreturn__));

The only argument is the return code of the function, as long as the second parameter in Pthread_join Thread_return is not NULL, this value is passed to Thread_return. Finally, one thread cannot be waited by multiple threads, or the first line that receives the signal Cheng returns, and the remaining thread that calls Pthread_join returns the error code esrch.

In this section, we have written one of the simplest threads and mastered the three most commonly used functions pthread_create,pthread_join and pthread_exit. Let's look at some of the common properties of threads and how to set these properties.

1. Modify the properties of the thread

In the example in the previous section, we created a thread with the Pthread_create function, where we used the default parameter to set the second argument of the function to null. Indeed, for most programs, using the default properties is enough, but we still need to know about the properties of the thread.

The property structure is pthread_attr_t, which is also defined in the header file/usr/include/pthread.h, and people who like inquisitive can see for themselves. The property value cannot be set directly, it must be manipulated using the correlation function, the function initialized is pthread_attr_init, and the function has to be called before the Pthread_create function. Property objects mainly include whether to bind, detach, stack address, stack size, priority. The default properties are unbound, non-detached, default 1M stacks, and the same level of precedence as the parent process.

The binding of threads involves another concept: light processes (lwp:light Weight process). A light process can be understood as a kernel thread, which is located between the user layer and the system layer. The system's allocation of thread resources, the control of threads is achieved through a light process, and a light process can control one or more threads. By default, how many light processes are started and which light processes control which threads are controlled by the system, which is called Unbound. Binding state, the name implies that a thread fixed "tied" on a light process. The bound thread has a high response speed because the CPU time slice is scheduled for a lightweight process, and the bound thread can guarantee that it always has a light process available when needed. By setting the priority and dispatch level of the lightweight process being bound, the bound thread can satisfy requirements such as real-time response.

The function that sets the thread binding state is Pthread_attr_setscope, which has two parameters, the first is a pointer to the property structure, the second is the binding type, and it has two values: Pthread_scope_system (bound) and Pthread_scope _process (non-binding). The following code creates a bound thread.

#include <pthread.h>

pthread_attr_t attr;

pthread_t Tid;

  

Pthread_attr_init (&ATTR);

Pthread_attr_setscope (&attr, Pthread_scope_system);

Pthread_create (&tid, &attr, (void *) my_function, NULL);

The separation state of a thread determines how a thread terminates itself. In the example above, we take the default attribute of the thread, which is the non-detached state, in which case the original thread waits for the thread to be created. Only when the Pthread_join () function returns does the thread created terminate to free the system resources that it occupies. The separation thread is not the case, it is not waiting for the other threads, the end of their own run, the thread is terminated, immediately release the system resources. Programmers should choose the appropriate separation state according to their own needs. The function to set the thread detach state is pthread_attr_setdetachstate (pthread_attr_t *attr, int detachstate). The second parameter can be selected as pthread_create_detached (Detach thread) and PTHREAD _create_joinable (non-detached thread). One thing to note here is that if a thread is set to detach a thread, and the thread runs very fast, it is likely to terminate before the Pthread_create function returns, and it may terminate the thread number and system resources for use by other threads, so that the call Pthread_ The thread of create gets the wrong thread number. To avoid this situation, one of the simplest ways to do this is to call the Pthread_cond_timewait function in the created thread to allow the thread to wait for a while, leaving enough time for the function pthread_create to return. Setting a wait time is a common method in multithreaded programming. But be careful not to use functions such as wait (), which make the whole process sleep, and do not resolve thread synchronization issues.

Another potentially common attribute is the priority of the thread, which is stored in the structure Sched_param. Using function Pthread_attr_getschedparam and function Pthread_attr_setschedparam to store, generally speaking, we always first take priority, the value of the obtained modified and then stored back. The following is a simple example.

#include <pthread.h>

#include <sched.h>

pthread_attr_t attr;

pthread_t Tid;

Sched_param param;

int newprio=20;

Pthread_attr_init (&ATTR);

Pthread_attr_getschedparam (&attr, &param);

Param.sched_priority=newprio;

Pthread_attr_setschedparam (&attr, &param);

Pthread_create (&tid, &attr, (void *) MyFunction, myarg);

2. Data processing of threads

One of the greatest advantages of threading compared to a process is the sharing of data, in which processes share data segments that are inherited by the parent process, and data can be easily obtained and modified. But this also brings a lot of problems to multithreaded programming. We must beware of having multiple different processes accessing the same variable. Many functions are non-reentrant, which means that multiple copies of a function cannot be run at the same time (unless different data segments are used). Static variables declared in a function often cause problems, and the return value of a function can be problematic. Because if the address of the space that is statically declared inside the function is returned, the other thread might call this function and modify the piece of data when a thread calls the function to get the address and uses the data that the address points to. variables shared in the process must be defined with the keyword volatile to prevent the compiler from changing how they are used when optimizing, such as using the-ox parameter in GCC. In order to protect variables, we must use semaphores, mutexes and other methods to ensure that the correct use of variables. Below, we'll step through the knowledge of threading data.

2, 1 thread data

In a single-threaded program, there are two basic types of data: global variables and local variables. In multithreaded programs, however, there is a third type of data: thread data (tsd:thread-specific). It is much like a global variable, inside a thread, functions can be called as if they were global variables, but it is invisible to other threads outside the thread . The need for such data is obvious. For example, our common variable, errno, returns a standard error message. It obviously cannot be a local variable, almost every function should be able to invoke it, but it cannot be a global variable, otherwise the output in a thread is likely to be a B-thread error message. To implement variables like this, we have to use thread data. We create a key for each thread data, which is associated with this key, which is used in each thread to refer to thread data, but in different threads, the key represents a different data, and in the same thread, it represents the same data content.

There are 4 main functions related to thread data: Create a key, specify thread data for a key, read thread data from a key, delete a key.

The function prototypes for creating keys are:

extern int pthread_key_create __p (pthread_key_t *__key,

void (*__destr_function) (void *)));

The first parameter is a pointer to a key value, the second parameter indicates a destructor function, and if the argument is not NULL, the system calls this function to release the block of memory bound to the key when each thread ends. This function is often used in conjunction with the function pthread_once ((Pthread_once_t*once_control, Void (*initroutine)), in order for the key to be created only once. The function pthread_once declares an initialization function that executes the function the first time it is called pthread_once, and the subsequent invocation is ignored by it.

In the following example, we create a key and associate it with some data. To define a function CreateWindow, this function defines a graphics window (data type Fl_window *, which is the data type in the graphical interface development tool FLTK). Because each thread calls this function, we use thread data.

  

pthread_key_t Mywinkey;

  

void CreateWindow (void) {

Fl_window * WIN;

static pthread_once_t once= pthread_once_init;

  

Pthread_once (& Once, Createmykey);

  

Win=new Fl_window (0, 0, +, +, "Mywindow");

  

SetWindow (Win);

  

Pthread_setspecific (Mywinkey, Win);

}

  

void Createmykey (void) {

Pthread_key_create (&mywinkey, Freewinkey);

}

  

void Freewinkey (Fl_window * win) {

Delete win;

}

In this way, calling the function Createmywin in different threads can get the window variables that are visible inside the thread, and this variable is obtained through the function pthread_getspecific. In the example above, we have used the function pthread_setspecific to bind the thread data with a key. The prototypes of these two functions are as follows:

extern int pthread_setspecific __p ((pthread_key_t __key,__const void *__pointer));

extern void *pthread_getspecific __p (pthread_key_t __key);

The parameter meanings and usage of these two functions are obvious. Note that when you use Pthread_setspecific to specify new thread data for a key, you must release the original thread data yourself to reclaim the space. This process function pthread_key_delete is used to delete a key, the memory that the key occupies will be freed, but it is also important to note that it only frees the memory used by the key, does not release the memory resource that is consumed by the thread data associated with the key, and it does not trigger the function Pthread_key_ The destructor function defined in Create. The release of the thread data must be completed before the key is released.

2.2 Mutual exclusion Lock

Mutexes are used to guarantee that only one thread is executing a piece of code over a period of time. The necessity is obvious: assuming that each thread writes data sequentially to the same file, the final result must be catastrophic.

Let's look at the following piece of code first. This is a read/write program that is common to a buffer, and we assume that a buffer can hold only one message. That is, the buffer has only two states: there is information or no information.

void reader_function (void);

void writer_function (void);

char buffer;

int buffer_has_item=0;

pthread_mutex_t Mutex;

struct TIMESPEC delay;

void Main (void) {

pthread_t reader;

  

Delay.tv_sec = 2;

Delay.tv_nec = 0;

  

Pthread_mutex_init (&mutex,null);

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) {

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);

}

}

The mutex variable mutex is declared here, and the struct pthread_mutex_t is a data type that is not exposed, which contains a system-assigned Property object. The function pthread_mutex_init is used to generate a mutex lock. A null parameter indicates that the default property is used. If you need to declare a mutex for a particular property, call the function Pthread_mutexattr_init. The function pthread_mutexattr_setpshared and the function Pthread_mutexattr_settype are used to set the Mutex property. The previous function sets the property pshared, which has two values, pthread_process_private, and pthread_process_shared. The former is used for thread synchronization in different processes, and the latter is used to synchronize different threads of the process. In the example above, we are using the default attribute Pthread_process_ PRIVATE. The latter is used to set the mutex type, and the optional types are pthread_mutex_normal, Pthread_mutex_errorcheck, pthread_mutex_recursive, and PTHREAD _mutex_default. They define the different mechanisms of the upper, the unlock, and, in general, the last default attribute.

The Pthread_mutex_lock declaration begins with a mutex lock, and thereafter the code is locked until the call Pthread_mutex_unlock, i.e. only one thread can invoke execution at the same time. When a thread executes to pthread_mutex_lock, if the lock is being used by another thread at this point, the thread is blocked, that is, the program waits for the other thread to release the mutex. In the example above, we used the PTHREAD_DELAY_NP function to get the thread to sleep for a while to prevent a thread from always occupying this function.

The above example is very simple, no longer introduced, it is necessary to use the mutex is likely to deadlock: two threads trying to occupy two resources at the same time, and in different order to lock the corresponding mutex, for example, two threads need to lock the mutex 1 and the mutex 2,a thread first lock the mutex lock 1, The b thread locks the mutex 2 first, and then a deadlock occurs. At this point we can use the function Pthread_mutex_trylock, which is a non-blocking version of the function Pthread_mutex_lock, when it finds that deadlock is unavoidable, it returns the appropriate information, and the programmer can handle the deadlock accordingly. In addition, different types of mutexes do not deal with deadlocks, but the main thing is for programmers to pay attention to this in their own programming.

2.3 Item Variables

In the previous section we talked about how to use mutexes to share and communicate data between threads, and one obvious disadvantage of mutexes is that it has only two states: locking and non-locking. The condition variable compensates for the lack of a mutex by allowing the thread to block and wait for another thread to send a signal, which is often used in conjunction with a mutex. When used, a condition variable is used to block a thread, and when the condition is not met, the thread often unlocks the corresponding mutex and waits for the condition to change. Once another thread changes the condition variable, it notifies the corresponding condition variable to wake one or more threads that are being blocked by this condition variable. These threads will re-lock the mutex and re-test whether the condition is satisfied. In general, conditional variables are used for synchronization between threads.

The structure of the condition variable is pthread_cond_t, and the function Pthread_cond_init () is used to initialize a condition variable. Its prototype is:

extern int Pthread_cond_init __p ((pthread_cond_t *__cond,__const pthread_condattr_t *__cond_attr));

Where cond is a pointer to the structure pthread_cond_t, cond_attr is a pointer to the structure pthread_condattr_t. The structure pthread_condattr_t is the property structure of the condition variable, and the same as the mutex we can use it to set whether the condition variable is available in process or between processes, the default value is Pthread_ process_private, That is, this condition variable is used by individual threads within the same process. Note that initialization condition variables can be reinitialized or released only if they are not in use. The function that releases a condition variable is pthread_cond_ destroy (pthread_cond_t cond).

The function pthread_cond_wait () causes the thread to block on a condition variable. Its function prototypes are:

extern int pthread_cond_wait __p (pthread_cond_t *__cond,

pthread_mutex_t *__mutex));

The thread unlocks the lock that the mutex points to and is blocked by the condition variable cond. The thread can be awakened by the function pthread_cond_signal and the function pthread_cond_broadcast, but it is important to note that the condition variable only acts as a blocking and wake-up thread, and the specific judging condition needs to be given by the user, such as whether a variable is 0, etc. We can see this in the following example. After the thread is awakened, it will recheck to see if the condition is satisfied, and if it is not satisfied, the thread should still be stuck here, waiting for the next wake-up. This process is typically implemented with a while statement.

Another function to block a thread is pthread_cond_timedwait (), which is a prototype:

extern int pthread_cond_timedwait __p (pthread_cond_t *__cond,

pthread_mutex_t *__mutex, __const struct timespec *__abstime);

It is more than a function pthread_cond_wait () a time parameter, after the abstime period of time, even if the condition variable is not satisfied, blocking is also lifted.

The prototype of the function pthread_cond_signal () is:

extern int pthread_cond_signal __p ((pthread_cond_t *__cond));

It is used to release a thread that is blocked on the condition variable cond. When multiple threads block on this condition variable, which thread is awakened is determined by the thread's scheduling policy. It is important to note that this function must be protected with a mutex that protects the condition variable, otherwise the condition satisfies the signal and may be emitted between the test condition and the call pthread_cond_wait function, resulting in an unrestricted wait. Here is a simple example of using the function pthread_cond_wait () and the function pthread_cond_signal ().

pthread_mutex_t Count_lock;

pthread_cond_t Count_nonzero;

unsigned count;

Decrement_count () {

Pthread_mutex_lock (&count_lock);

while (count==0)

Pthread_cond_wait (&count_nonzero, &count_lock);

count=count-1;

Pthread_mutex_unlock (&count_lock);

}

Increment_count () {

Pthread_mutex_lock (&count_lock);

if (count==0)

Pthread_cond_signal (&count_nonzero);

count=count+1;

Pthread_mutex_unlock (&count_lock);

}

When the count value is 0 o'clock, the decrement function is blocked at pthread_cond_wait and open the mutex count_lock. At this point, when called to the function Increment_count, the pthread_cond_signal () function changes the condition variable to tell Decrement_count () to stop blocking. The reader can try to get two threads to run the two functions separately to see what happens.

The function pthread_cond_broadcast (pthread_cond_t *cond) is used to wake up all threads that are blocked on the condition variable cond. These threads are awakened and will compete for the corresponding mutex again, so you must use this function with care.

2, 4 signal volume

The semaphore is essentially a nonnegative integer counter that is used to control access to public resources. When the public resources increase, the call function Sem_post () increases the semaphore. A public resource can be used only if the semaphore value is greater than 0 o'clock, and the function sem_wait () reduces the semaphore after use. The function sem_trywait () and the function Pthread_ Mutex_trylock () play the same role, which is the non-blocking version of the function sem_wait (). Here we describe some of the functions related to semaphores, which are defined in the header file/usr/include/semaphore.h.

The data type of the semaphore is the structure sem_t, which is essentially a number of long integers. The function Sem_init () is used to initialize a semaphore. Its prototype is:

extern int Sem_init __p ((sem_t *__sem, int __pshared, unsigned int __value));

SEM is a pointer to a semaphore structure; pshared is not 0 o'clock this semaphore is shared between processes, otherwise it can only be shared for all threads of the current process; value gives the initial value of the semaphore.

The function sem_post (sem_t *sem) is used to increase the value of the semaphore. When a thread is blocked on this semaphore, calling this function causes one of the threads to not block, and the selection mechanism is also determined by the thread's scheduling policy.

The function sem_wait (sem_t *sem) is used to block the current thread until the value of the semaphore SEM is greater than 0, and the value of the SEM is reduced by one after unblocking, indicating that the public resources are decreased after use. The function sem_trywait (sem_t *sem) is a non-blocking version of the function sem_wait (), which directly minimizes the value of the semaphore SEM.

The function Sem_destroy (sem_t *sem) is used to release the signal volume of the SEM.

Let's take a look at an example of using semaphores. In this example, there are 4 threads, two of which are responsible for reading data from a file to a common buffer, and two threads reading data from the buffer for different processing (plus and multiply operations).

  

#include <stdio.h>

#include <pthread.h>

#include <semaphore.h>

#define MAXSTACK 100

int stack[maxstack][2];

int size=0;

sem_t sem;

  

void ReadData1 (void) {

FILE *fp=fopen ("1.dat", "R");

while (!feof (FP)) {

FSCANF (FP, "%d%d", &stack[0],&stack[1]);

Sem_post (&sem);

++size;

}

Fclose (FP);

}

  

void ReadData2 (void) {

FILE *fp=fopen ("2.dat", "R");

while (!feof (FP)) {

FSCANF (FP, "%d%d", &stack[0],&stack[1]);

Sem_post (&sem);

++size;

}

Fclose (FP);

}

  

void HandleData1 (void) {

while (1) {

Sem_wait (&sem);

printf ("plus:%d+%d=%d\n", stack[0],stack[1],

STACK[0]+STACK[1]);

--size;

}

}

void HandleData2 (void) {

while (1) {

Sem_wait (&sem);

printf ("multiply:%d*%d=%d\n", stack[0],stack[1],

STACK[0]*STACK[1]);

--size;

}

}

int main (void) {

pthread_t T1,t2,t3,t4;

Sem_init (&sem,0,0);

Pthread_create (&t1,null, (void *) handledata1,null);

Pthread_create (&t2,null, (void *) handledata2,null);

Pthread_create (&t3,null, (void *) readdata1,null);

Pthread_create (&t4,null, (void *) readdata2,null);

  

Pthread_join (T1,null);

}

Under Linux, we use the command gcc-lpthread sem.c-o sem to generate the executable file SEM. We edit the data files in advance 1.dat and 2.dat, assuming that their content is 1 2 3 4 5 6 7 8 9 10 and-1-2-3-4-5-6-7-8-9-10, we run the SEM, get the following results:

multiply:-1*-2=2

Plus:-1+-2=-3

Multiply:9*10=90

Plus:-9+-10=-19

multiply:-7*-8=56

Plus:-5+-6=-11

Multiply:-3*-4=12

Plus:9+10=19

Plus:7+8=15

plus:5+6=11

From this we can see the competitive relationship between the various threads. And the values are not shown in our original order. This is because the value of size is arbitrarily modified by each thread. This is also often the problem of multithreading programming to pay attention to.

3. Summary

Multithreaded programming is a very interesting and useful technology, the use of multi-threaded network ant is one of the most commonly used download tools, using multi-threading technology grep than single-threaded grep a few times faster, similar examples are many. I hope you can use multithreading technology to write efficient and practical good program.

Reference:

Http://baike.baidu.com/view/400319.htm

Http://hi.baidu.com/xiapingwen/blog/item/2d99c73883c3b9c8d4622531.html

<pthread.h> of Linux;

Related Article

E-Commerce Solutions

Leverage the same tools powering the Alibaba Ecosystem

Learn more >

Apsara Conference 2019

The Rise of Data Intelligence, September 25th - 27th, Hangzhou, China

Learn more >

Alibaba Cloud Free Trial

Learn and experience the power of Alibaba Cloud with a free trial worth $300-1200 USD

Learn more >

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.