LINUX multi-thread design

Source: Internet
Author: User

# Include <pthread. h>

1. Create int pthread_create (pthread_t * tid, const pthread_attr_t * ATTR, void * (* func) (void *), void * Arg); ATTR: thread attributes include: priority, initial stack size, whether it should be a daemon thread. By default, null is followed by the functions and parameters to be executed by the thread. 02 is returned successfully. Wait for a given thread to terminate int pthread_join (pthread_t tid, void ** status ); statues returns the return value of the waiting thread 3. obtains its own pidpthread_t pthread_self (void); 4. pthread_detach function int pthread_detach (pthread_t PID ); converts a specified thread to a detached thread, a conflustered thread (joinable, default value), or a detached thread (detached ). When a conflustered thread terminates, its thread ID and exit state will be left to another thread to call pthread_join for it. The disconnection from threads is like a daemon process: When they terminate, all related resources are released and we cannot wait for them to terminate. If one thread needs to know when the other thread will terminate, it is better to enjoy the confluence status of the second thread. This function is usually called by a thread that wants to disassociate itself. The following statement is pthread_detach (pthread_self (). 5. terminate a thread void pthread_exit (void * Statue ); the pointer sttus cannot point to a local call object, because such an object disappears at the end of the thread. thread attribute settings
We use the pthread_create function to create a thread. In this thread, we use the default parameter to set the second parameter of the function to null. Indeed, for most programs, it is enough to use the default attribute, but we still need to understand the relevant attributes of the thread.
The property structure is pthread_attr_t, which is also in the header file pthread. as defined in H, the attribute value cannot be directly set and relevant functions must be used for operations. The initialized function is pthread_attr_init, which must be called before the pthread_create function. Attribute objects mainly include binding, splitting,
Stack address, stack size, and priority. Default attributes are non-bound, non-separated, default stacks, and priorities of the same level as the parent process. 1. Bind
Thread binding involves another concept: lwp: Light Weight process ). A lightweight process can be understood as a kernel thread, which is located between the user layer and the system layer. The system allocates thread resources and controls threads through lightweight processes. A lightweight process can control one or more threads. By default, the number of light processes started and the light processes to control which threads are controlled by the system are called unbound. Under the binding condition, a thread is bound to a light process. The bound thread has a high response speed because the CPU time slice is scheduled to light processes. The bound thread can ensure that there is always a light process available when needed. By setting the priority and scheduling level of the bound process, the bound thread can meet requirements such as real-time response.
The function used to set the thread binding status is pthread_attr_setscope. It has two parameters: the first is the pointer to the attribute structure, and the second is the binding type. It has two values: pthread_scope_system (bound) and pthread_scope_process (unbound ). The following code creates a bound thread.
# I nclude <pthread. h>
Pthread_attr_t ATTR;
Pthread_t tid;
/* Initialize the property value, which is set to the default value */
Pthread_attr_init (& ATTR );
Pthread_attr_setscope (& ATTR, pthread_scope_system );
Pthread_create (& tid, & ATTR, (void *) my_function, null); 2. The separation status of a thread in the thread separation status determines how a thread terminates itself. When a non-detached thread terminates, its thread ID and exit status will be retained until another thread calls pthread_join. when a detached thread terminates, all resources are released and we cannot wait for it to terminate. The function for setting the thread separation status is pthread_attr_setdetachstate (pthread_attr_t * ATTR, int detachstate ). The second parameter can be pthread_create_detached and pthread _ create_joinable ). Note that if you set a thread as a separate thread and the thread runs very fast, it is likely to terminate before the pthread_create function returns, after it is terminated, it may hand over the thread number and system resources to other threads for use. In this way, the thread that calls pthread_create gets the wrong thread number. To avoid this situation, you can take some synchronization measures. One of the simplest methods is to call the pthread_cond_timewait function in the created thread, so that the thread can wait for a while, leave enough time for the function pthread_create to return. Setting a wait time is a common method in multi-threaded programming.
3. The priority value is stored in the schema sched_param. Use the pthread_attr_getschedparam function and the pthread_attr_setschedparam function to store the data. Generally, we always take the priority and modify the obtained value before storing it back. The following is a simple example. # I nclude <pthread. h>
# I nclude <sched. h>
Pthread_attr_t ATTR; pthread_t tid;
Sched_param Param;
Int newprio = 20;
/* Initialize attributes */
Pthread_attr_init (& ATTR );
/* Set priority */
Pthread_attr_getschedparam (& ATTR, & PARAM );
Param. sched_priority = newprio;
Pthread_attr_setschedparam (& ATTR, & PARAM );
Pthread_create (& tid, & ATTR, (void *) myfunction, myarg); 4. comparing thread data processing with processes, one of the biggest advantages of threads is data sharing. Each process shares the data segment that follows the parent process to conveniently obtain and modify data. But this also brings many problems to multithreaded programming. We must be careful that there are multiple different processes accessing the same variable. Many functions cannot be reentrant, that is, they cannot run multiple copies of a function at the same time (unless different data segments are used ). Static variables declared in functions often cause problems and return values of functions. If the returned address is the address of the Space statically declared by the function, when a thread calls the function to obtain the address and uses the data pointed to by the address, other threads may call this function and modify this data segment. Shared variables must be defined with the keyword volatile in the process to prevent the compiler from changing their usage methods during optimization (for example, using the-ox parameter in GCC. To protect variables, we must use semaphores, mutex, and other methods to ensure correct use of variables.
1. There are two basic types of data in a single-threaded program: global variables and local variables. However, in multi-threaded programs, there is also a third data type: thread data (TSD: thread-specific data ). It is similar to a global variable. In a thread, each function can call it like a global variable, but it is invisible to other threads outside the thread. For example, the common variable errno returns standard error information. Obviously, it cannot be a local variable. Almost every function can call it, but it cannot be a global variable, otherwise, the error message of line B may be output in line. To implement such variables, we must use thread data. We create a key for each thread data, which is associated with this key. In each thread, this key is used to represent the thread data, but in different threads, this key represents different data. In the same thread, it represents the same data content.
There are four main functions related to thread data: Create a key, specify thread data for a key, read thread data from a key, and delete a key.
The function prototype for creating a key is:
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 specifies a destructor function. If this parameter is not empty, the system will call this function when each thread ends to release the memory block bound to this key. This function is often used with the function pthread_once (pthread_once_t * once_control, void (* initroutine) (void) to create this key only once. The pthread_once function declares an initialization function. When pthread_once is called for the first time, it executes this function and will be ignored in future calls.
Int pthread_key_delete (pthread_key_t * Key );
This function is used to delete a key created by calling the pthread_key_create function. If the call succeeds, the return value is 0. Otherwise, the error code is returned.
In the following example, we create a key and associate it with a certain data. We need to define a function createwindow, which defines a graphical window (the data type is fl_window *, which is the data type in the graphic interface development tool fltk ). Since each thread calls this function, we use thread data.
/* Declare a key */
Pthread_key_t mywinkey;
/* Function createwindow */
Void createwindow (void ){
Fl_window * Win;
Static pthread_once_t once = pthread_once_init;
/* Call the createmykey function to create a key */
Pthread_once (& once, createmykey );
/* Win points to a new window */
Win = new fl_window (0, 0,100,100, "mywindow ");
/* Make some possible settings for this window, such as the size, position, and name */
Setwindow (WIN );
/* Bind the window pointer value to the key mywinkey */
Pthread_setpecific (mywinkey, Win );
}
/* The createmykey function creates a key and specifies the Destructor */
Void createmykey (void ){
Pthread_keycreate (& mywinkey, freewinkey );
}
/* Function freewinkey, release space */
Void freewinkey (fl_window * Win ){
Delete win;
}
In this way, the createmywin function can be called in different threads to obtain window variables that can be seen inside the thread. This variable is obtained through the pthread_getspecific function. In the above example, we have used the pthread_setspecific function to bind thread data with a key. The two functions are prototype as follows:
  
Int pthread_setspecific _ p (pthread_key_t _ key ,__ const void * _ pointer); this function sets the value of a thread-specific data and assigns it to the key created by pthread_key_create, if the call succeeds, the return value is 0. Otherwise, the error code is returned.
Void * pthread_getspecific _ p (pthread_key_t _ key); this function obtains the value bound to the specified key. If the call is successful, the data corresponding to the given parameter key is returned. If no data is connected to the key, null is returned. The parameter meanings and usage of these two functions are obvious. Note that when pthread_setspecpacific is used as a key to specify new thread data, the original thread data must be released to recycle space. This process function pthread_key_delete is used to delete a key. The memory occupied by this key will be released, but note that it only releases the memory occupied by the key, does not release the memory resources occupied by the thread data associated with the key, and it does not trigger the destructor function defined in the pthread_key_create function. The release of thread data must be completed before the release key.
2. mutex locks assume that each database writes data to the same file in sequence, and the final result is unimaginable. Therefore, the mutex lock is used to ensure that only one thread is executing a piece of code within a period of time. Use int pthread_mutex_lock to lock the mutex and use int pthread_mutex_unlock to unlock the lock.
If we try to lock a mutex lock that has been locked by another thread, the program will block until the mutex object is unlocked.
If a mutex lock is assigned to the shared memory, we must call the ptgread_mutex_init function to initialize the lock at runtime.
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;
/* Define the latency */
Delay. TV _sec = 2;
Delay. TV _nec = 0;
/* Use the default attribute to initialize a mutex lock object */
Pthread_mutex_init (& mutex, null );
Pthread_create (& reader, pthread_attr_default, (void *) & reader_function), null );
Writer_function ();
}
Void writer_function (void ){
While (1 ){
/* Lock the mutex lock */
Pthread_mutex_lock (& mutex );
If (buffer_has_item = 0 ){
Buffer = make_new_item ();
Buffer_has_item = 1;
}
/* Open the mutex lock */
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 pthread_mutex_init function is used to generate a mutex lock. The null parameter indicates that the default attribute is used. To declare a mutex lock for a specific attribute, call the pthread_mutexattr_init function. The pthread_mutexattr_setpshared function and the pthread_mutexattr_settype function are used to set the mutex lock attribute. The previous function sets the property pshared, which has two values: pthread_process_private and pthread_process_shared. The former is used to synchronize threads in different processes, and the latter is used to synchronize different threads in the process. In the preceding example, we use the default pthread_process _ private attribute. The latter is used to set mutex lock types. Optional types include pthread_mutex_normal, pthread_mutex_errorcheck, pthread_mutex_recursive, and pthread _ mutex_default. They define different on-board and unlock mechanisms. Generally, the last default attribute is used.
It is important to note that a deadlock may occur when mutex lock is used: two threads attempt to occupy two resources at the same time and lock the corresponding mutex lock in different order, for example, both threads need to lock mutex lock 1 and mutex lock 2. Thread a First locks mutex lock 1, thread B First locks mutex lock 2, and a deadlock occurs. In this case, we can use the function pthread_mutex_trylock, which is a non-blocking version of the function pthread_mutex_lock. When it finds that a deadlock is inevitable, it will return the corresponding information, and the programmer can handle the deadlock accordingly. In addition, different mutex lock types have different deadlocks, but the most important thing is that programmers should pay attention to this in programming.
3. Condition Variables
An obvious disadvantage of mutex is that it has only two States: Locked and non-locked. Conditional variables make up for the lack of mutex lock by allowing the thread to block and wait for another thread to send signals. They are often used together with mutex locks. When a condition variable is used to block a thread, 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 will notify the corresponding condition variable to wake up one or more threads that are blocked by this condition variable. These threads will re-lock the mutex and re-test whether the conditions are met. In general, condition variables are used for line-to-line synchronization.
The condition variable structure is pthread_cond_t. The pthread_cond_init () function is used to initialize a condition variable. Its prototype is: int pthread_cond_init _ p (pthread_cond_t * _ cond ,__ const pthread_condattr_t * _ cond_attr ));

Cond is a pointer to the structure pthread_cond_t, and cond_attr is a pointer to the structure pthread_condattr_t. The structure pthread_condattr_t is the attribute structure of the condition variable. Like the mutex lock, we can use it to set whether the condition variable is available in the process or between processes. The default value is pthread _ process_private, this condition variable is used by various threads in the same process. Note that the initialization condition variables can be reinitialized or released only when they are not used.
In pthread, the condition variable is a variable of the pthread_cond_t type. The condition variable uses the following two functions: the pthread_cond_wait function is used for blocking. The thread can be awakened by the pthread_cond_signal function and the pthread_cond_broadcast function, however, it should be noted that the condition variable only serves to block and wake up the thread. The specific judgment condition must be provided by the user, for example, whether a variable is 0, we can see this in the following example. After the thread is awakened, it will re-check whether the conditions are met. If the conditions are not met, the thread should still be blocked here and be waiting for the next wake-up. This process is generally implemented using the while statement.
Another function used to block a thread is pthread_cond_timedwait (). It has one more time parameter than the pthread_cond_wait () function. After a time period of abstime, even if the condition variable is not met, the blocking is also removed.
The pthread_cond_signal () function is used to release a thread that is blocked on the condition variable cond.
The pthread_cond_broadcast (pthread_cond_t * Cond) function is used to wake up all threads blocked on the condition variable cond. After these threads are awakened, they will compete for the corresponding mutex lock again. Therefore, you must use this function with caution.
The following is a simple example of using the functions pthread_cond_wait () and 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, the decrement function is blocked at pthread_cond_wait and the mutual exclusion lock count_lock is enabled. At this time, when the increment_count function is called, The pthread_cond_signal () function changes the condition variable and informs decrement_count () to stop blocking.

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.