Multi-threaded programming under Linux

xiaoxiao2021-03-06  19

1 Introduction

Thread technology has been proposed in the 1960s, but the truly used multi-threaded to the operating system is in the mid-1980s, Solaris is the leader in this area. Traditional UNIX also supports the concept of threads, but only one thread is allowed in a process, so many threads means multiple processes. Now, multi-threading technology has been supported by many operating systems, including Windows / NT, of course, including Linux.

Why after the concept of the process, will you introduce a thread? What are the benefits of using multiple threads? What should I choose multithreaded? We must first answer these questions.

One of the reasons for using multithreades is a very "thrifty" multitasking mode compared to the process. We know, under the Linux system, start a new process must be assigned to its separate address space, build a large number of data sheets to maintain its code segment, stack segment, and data segment, this is a "expensive" multitasking Way of working. The multiple threads running in a process are used to use the same address space between each other. Sharing most of the data, starting a spatial space spent on a thread is far less than the space spent on the start of a process, and between the threads The required time is far less than the time required for the process to switch between the processes. According to statistics, it is always said that the overhead of a process is about 30 times that of a thread overhead. Of course, this data may have a big difference on a specific system.

The second reason to use multithreaded is the convenient communication mechanism between threads. For different processes, they have independent data spaces, and the delivery of data can only be carried out by communication, not only the time, but it is very inconvenient. The thread is not, because the data space is shared between the threads under the same process, so the data of a thread can be used directly to other threads, which is not only fast, and convenient. Of course, the sharing of data also brings some other problems, and some variables cannot be modified by two threads at the same time. Some subroutines are declared to static data more likely to bring catastrophic strikes to multi-threaded programs. These positive It is the most needed place when writing multithreaded programs.

In addition to the advantages mentioned, the multithreading procedure is a multi-task, and the multithreaded procedure is of course the following advantages:

1) Improve the application response. This is especially meaningful to the graphical interface. When a time consumption is long, the entire system will wait for this operation. At this time, the program will not respond to the keyboard, mouse, the menu operation, and use multi-threaded technology, will take time long. Time consuming is placed in a new thread to avoid this embarrassment.

2) Make more CPU systems more effective. The operating system ensures that different threads run on different CPUs when the number of threads is not greater than the CPU.

3) Improve the program structure. A long and complicated process can be considered to be divided into multiple threads, becoming several independent or semi-independent operational parts, which will benefit and modify.

Let's first try to write a simple multi-threaded program.

2 Simple multi-threaded programming

Multithreading under the Linux system follows the POSIX thread interface, called PTHREAD. Write a multi-threaded program under Linux, you need to use the header file pthread.h, and you need to use the library libpthread.a when connecting. By the way, the implementation of PTHREAD under Linux is implemented by the system call clone (). Clone () is a system call unique to Linux. Its usage mode is similar to fork, about clone (), interested readers can see the document description. Below we show a simplest multi-threaded program example1.c. / * eXample.c * /

#include

#include

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 procedure:

GCC EXAMPLE1.C -LPTHREAD -O EXAMPLE1

Run EXAMPLE1, we get the following results:

THIS II Main Process.

THIS is a pthread.

THIS II Main Process.

THIS II Main Process.

THIS is a pthread.

THIS is a pthread.

Run again, we may get the following results:

THIS is a pthread.

THIS II Main Process.

THIS is a pthread.

THIS II Main Process.

THIS is a pthread.

THIS II Main Process.

The results are different before and after, this is the result of two threads compete for the CPU resource. In the above example, we used two functions, pthread_create, and pthread_join, and declare a PTHREAD_T type variable.

PTHREAD_T defines in header /usr/includes/bits/pthreadtypes.h:

TYPEDEF UNSIGNED Long Int Pthread_t;

It is an identifier of a thread. The function pthread_create is used to create a thread, and its prototype is:

Extern int pthread_create __p ((pthread_t * __ thread, __const pthread_attr_t * __ attr,

Void * (* __ start_routine) (void *), void * __ arg);

The first parameter is a pointer to the thread identifier, and the second parameter is used to set the thread attribute. The third parameter is the start address of the thread run function, and the last parameter is the parameter of the run function. Here, our function thread does not require parameters, so the last parameter is set to an empty pointer. The second parameter we also set to an empty pointer, which will generate threads of the default attribute. We will be elaborated in the next section on thread attributes. When the thread is created, the function returns 0. If it is not 0, the creation thread fails, and the common error returns the code to eagain and EinVal. The former indicates that the system restrictions create new threads, such as excessive threads; the latter represents the thread attribute values ​​represented by the second parameter illegal. After creating a thread, the newly created thread runs the function of the parameter three and the parameter 4, and the original thread continues to run the next line of code. The function pthread_join is used to wait for a thread to end. The function is:

Extern int pthread_join __p (pthread_t __th, void ** __ thread_return);

The first parameter is the waiting thread identifier, the second parameter is a user-defined pointer, which can be used to store the return value of the waiting thread. This function is a thread blocking function that calls its function to wait until the end of the waiting thread, when the function returns, the resource that is waiting for a thread is retracted. There are two ways to end the thread, one is the same as the example above, the function is over, the thread that calls it is over; another way is to be implemented by function pthread_exit. Its function prototype is:

EXTERN VOID PTHREAD_EXIT __P ((void * __ retval) __ATtribute__ ((__noreturn__));

The unique parameter is the return code of the function, as long as the second parameter thread_return in pthread_join is not null, this value will be passed to Thread_Return. Finally, the thread cannot be waited by multiple threads, otherwise the first thread that receives the signal is successfully returned, and the thread of the remaining call pthread_join returns an error code ESRCH.

In this section, we wrote a simple thread and mastered the most commonly used three functions pthread_create, pthread_join and pthread_exit. Below, let's understand some common properties of the thread and how to set these properties.

3 Modify the properties of threads

In the example in the previous section, we created a thread with the pthread_create function. In this thread, we used the default parameters, which is the second parameter of the function to NULL. Indeed, for most programs, using the default attribute is enough, but we still need to understand the relevant properties of the thread.

The attribute structure is pthread_attr_t, which is also defined in header file /usr/include/pthread.h, people who like to chase the roots can check themselves. Attribute values ​​cannot be set directly, you must use the relevant function to operate, the initialization function is pthread_attr_init, which must be called before the pthread_create function. Attribute objects mainly include whether it is bound, whether it is separated, stack address, stack size, priority. The default attribute is non-bound, non-separated, default 1M stack, the same level of the same level as the parent process.

About thread bindings involving another concept: Light process (LWP: Light Weight Process). The light process can be understood as the kernel thread, which is located between the user layer and the system layer. The system is allocated to thread resources, and the control of thread is implemented by light processes, and a light process can control one or more threads. In the default, how many light processes are started, which threads are controlled by the system, which is called unbound. Under the binding situation, as the name suggests, the "tied" fixed "tied" is above a light process. The bound thread has a high response speed because the CPU time slogging is to face light processes, and the bound thread can be guaranteed to have a light process available when needed. The priority and scheduling level of the binding light process can make the bound thread meet the requirements such as real-time reactions. Set the function of the thread bind state is pthread_attr_setscope, which has two parameters, the first is a pointer to the attribute structure, the second is the binding type, which has two values: pthread_scope_system (bind) and pthread_scope_process (bind) and pthread_scope_process Unbound). The following code creates a binding thread.

#include

PTHREAD_ATTR_T ATTR;

Pthread_t Tid;

/ * Initialization attribute value, all set to default * /

Pthread_attr_init (& Attr);

PTHREAD_ATTR_SETSCOPE (& Attr, Pthread_scope_system);

Pthread_create (& TID, & Attr, (void *) my_function, null;

The separation state of the thread determines a thread to terminate yourself. In the above example, we use the default attribute of the thread, which is a non-separated state, in which case the original thread waits for the end of the thread. Only when the pthread_join () function returns, the created thread is terminated to release the system resources you usually take. The separation thread is not like this, it is not waiting for other threads, the end of the run, the thread is terminated, and the system resources are released immediately. Programmers should choose the appropriate separation state according to their own needs. Set the function of the thread separation state is pthread_attr_setdetachState (pthread_attr_t * attr, int debachstate). The second parameter is optional as pthread_create_detached (separation thread) and pthread _create_joinable. What you should pay attention to here is that if you set a thread to be separated thread, this thread runs very quickly, it is likely to terminate before the pthread_create function returns, it may hand over the thread number and system resources after termination. Thread use, thus calling pthread_create thread gets the wrong thread number. To avoid this situation, you can take certain synchronization measures. One of the simplest methods is that the pthread_cond_timewait function can be called in the created thread, so that this thread is waiting for a while, leave enough time to return the function pthread_create. Set a wait time, is a common method in multi-threaded programming. Be careful not to use functions such as Wait (), they are sleeping the entire process and does not solve the problem of thread synchronization.

Another possible property is the priority of the thread, which is stored in the structure SCHED_PARAM. Saving with functions pthread_attr_getschedparam and functions pthread_attr_setschedparam Here is a simple example. #include

#include

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;

4 thread data processing

Compared with the process, one of the biggest advantages of the thread is the sharing of data, and the data segments of each process share the parent processes, which can be easily obtained and modified. But this also brings many problems to multi-threaded programming. We have to beware of multiple different processes to access the same variable. Many functions are not renewable, namely multiple copies of a function (unless different data segments) are used. Static variables declared in functions often bring problems, and the return value of the function will also have problems. Because if returned is the address of the static declaration of the function, when a thread calls the function to get the address, the address points to data, the other threads may call this function and modify this segment. The variables shared in the process must be defined with the keyword volatile, which is to prevent the compiler from being optimized (such as using the -Ox parameter in the GCC) to change their usage. To protect the variables, we must use semaphores, mutual exclusion methods to ensure that we use the correct use of variables. Below, we gradually introduce relevant knowledge when processing thread data.

4.1 thread data

In a single-threaded program, there are two basic data: global variables and local variables. But in multithreaded programs, there are also third data types: thread data (TSD: Thread-Specific Data). Its and global variables are very elephant. Inside the thread, each function can call it like global variables, but it is invisible to other threads outside the thread. The necessity of this data is obvious. For example, our common variable errno, it returns the standard error message. It obviously can't be a local variable, almost every function should call it; but it can't be a global variable, otherwise it is probably the error information of the B thread in the A-thread. To implement the variables such as this, we must use thread data. We create a key for each thread data, it is associated with this button. In each thread, this button is used to refer to thread data, but in different threads, the data represented by this key is different. In a thread, it represents the same data content.

And thread data related functions have four: create a key; specify thread data for a key; read thread data from one key; delete key.

The function prototype of the creation button is:

EXTERN INT PTHREAD_KEY_CREATE __P ((pthread_key_t * __ key,

VOID (* __ destr_function))))));

The first parameter is a pointer to a key value. The second parameter indicates a DEStructor function. If this parameter is not empty, then when each thread is end, the system will call this function to release the binding on this button. The memory block. This function is often used with the function pthread_once (pthread_once_t * overce_control, void (* initRoutine))), in order to make this button only created once. The function pthread_once declares an initialization function. When you call PTHREAD_ONCE for the first time, it performs this function, and the future call will be ignored. In the following example, we create a key and associate it with a certain data. We want to define a function createWindow, which defines a graphics window (data type is fl_window *, which is the data type in the Graphical Interface Development Tool FLTK). This function is called because each thread is called, so we use thread data.

/ * Declare a key * /

Pthread_Key_t mywinkey;

/ * Function CREATEWINDOW * /

Void CreateWindow (Void) {

FL_Window * Win;

Static pthread_once_t overcha = pthread_once_init;

/ * Call the function createmyKey, create a key * /

Pthread_once (& Once, CREATEMYKEY);

/ * WIN points to a newly established window * /

WIN = New fl_window (0, 0, 100, 100, "myWindow");

/ * Do some possible settings for this window, such as size, location, name, etc. * /

Setwindow;

/ * Bind the window pointer value on the key mywinkey * /

Pthread_setpecific (MyWINKEY, WIN);

}

/ * Function CreatemyKey, create a key and specify DESTRUCTOR * /

Void createmyKey (void) {

Pthread_KeyCreate (& MyWINKEY, FREEWINKEY);

}

/ * Function freewinkey, release space * /

Void freewinkey (fl_window * win) {

Delete Win;

}

In this way, the function CreatemyWin can be called in different threads, and the window variables visible inside the thread can be obtained, and this variable is obtained by the function pthread_getspecific. In the above example, we have used functions PTHREAD_SETSPECific to bind thread data and a key. The prototype of these two functions is as follows:

EXTERN INT PTHREAD_SETSPECICICICIC __P ((pthread_key_t __key, __ const void * __poin);

EXTERN VOID * pthread_getspecific __p ((pthread_key_t __key));

The parameters and methods of use of these two functions are obvious. It should be noted that when using PTHRead_setspecific to specify a new thread data for a key, you must release the original thread data to reclaim the space. This process function pthread_key_delete is used to delete a key, and the memory occupied by this key will be released, but it should also be noted 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 function pthread_key_create. The release of thread data must be done before the release button.

4.2 mutex lock

Mutually exclusive locks to ensure that only one thread is executed for a period of time. The necessity is obvious: assume that each thread writes data to the same file, and finally the result is certain. Let's take a look at the following code. This is a read / write program that uses a buffer and we assume that a buffer can only save a message. That is, the buffer is only two status: 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;

/ * Define delay time * /

Delay.tv_sec = 2;

DELAY.TV_NEC = 0;

/ * Initialize a mutual repelo object with the default attribute * /

Pthread_mutex_init (& Mutex, Null);

Pthread_create (& Reader, pthread_attr_default, (void *) & reader_function), NULL);

Writer_function ();

}

Void Writer_Function (void) {

While (1) {

/ * Lock mutually exclusive lock * /

PTHREAD_MUTEX_LOCK (& MUTEX);

IF (buffer_has_item == 0) {

Buffer = make_new_item ();

Buffer_has_item = 1;

}

/ * Open mutex * /

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

}

}

Here, the mutex variable MUTEX, the structure PTHREAD_MUTEX_T is the non-disclosed data type, which contains a system allocated attribute object. The function pthread_mutex_init is used to generate a mutex. NULL parameters indicate the use of default properties. If you need to declare a mutex lock, you must call the function pthread_mutexattr_init. Function pthread_mutexattr_setpshared and functions PTHREAD_MUTEXATTR_SETTYPE Used to set mutex properties. The previous function sets the properties 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 the different threads of this process. In the above example, we use the default attribute pthread_process_produate. The latter is used to set the mutex type, and the optional type has pthread_mutex_normal, pthread_mutex_rectect, pthread_mutex_recursive and pthread _mutex_default. They define different above, unlock mechanisms, in general, select the last default attribute.

PTHREAD_MUTEX_LOCK declaration begins with a mutually exclusive lock, after which the code is called until pthread_mutex_unlock, it is locked, which can only be modified by one thread at the same time. When a thread is executed to the pthread_mutex_lock, if the lock is used by another thread, this thread is blocked, that is, the program will wait until another thread releases this mutex. In the above example, we use the pthread_delay_np function, let the thread sleep for a while, just to prevent a thread from always occupying this function. The above example is very simple, it is no longer introduced, it is necessary to propose that there is a deadlock in the process of using the mutex: two threads at the same time, and lock according to different order. Mutually exclusive locks, such as two threads need to lock mutex 1 and mutex 2, and the A thread is first locked in the mutex 1, and the B thread locks the mutex 2, and there is a deadlock. At this time we can use the function pthread_mutex_trylock, it is a non-blocking version of the function pthread_mutex_lock, when it is unavoidable, it returns the corresponding information, and the programmer can make a corresponding processing for the deadlock. In addition, different mutex types are different for deadlocks, but the main thing is to be programmers to pay attention to this in programming.

4.3 Conditional variables

In the previous section, we talked about how to use mutex to achieve sharing and communication between lines, and a remarkable disadvantage of mutex is that it has only two states: locking and non-locking. The condition variables make up for the insufficient mutex lock by allowing the thread blocking and waiting for another thread to send signals, which often uses the mutex. When used, the condition variable is used to block a thread. When the condition is not met, the thread tends to unlock the corresponding mutex and waits for changes. Once other thread changes the conditional variable, it will notify the corresponding condition variable to wake one or more threads that are being blocked by this condition variable. These threads will reslide the mutex lock and re-test whether the conditions are met. In general, the conditional variable is used to perform synchronization between the linear.

The structure of the condition variable is pthread_cond_t, and the function pthread_cond_init () is used to initialize a conditional 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 structural pthread_cond_t, COND_ATTR is a pointer to the structure pthread_condattr_t. Structure PTHREAD_CONDATTR_T is the properties structure of condition variables, and the mutex is the same. We can use it to set the condition variable is available or within the process, and the default value is pthread_ process_private, that is, this condition variable is used by each thread in the same process. Note that the initialization condition variable can be reincarily or released when it is not used. The function that releases a conditional variable is pthread_cond_ destroy (pthread_cond_t cond).

Functions pthread_cond_wait () encloses threads on a conditional variable. Its function prototype is:

EXTERN INT PTHREAD_COND_WAIT __P ((pthread_cond_t * __C,

PTHREAD_MUTEX_T * __ MUTEX));

The thread unchorbs the lock pointed to by MUTEX and is blocked by the condition variable. The thread can be woken up by the function pthread_cond_signal and function pthread_cond_broadcast, but it is to be noted that the conditional variable is just the role of blocking and wake-up threads, and the specific judgment criterion requires the user to give it, such as whether a variable is 0, etc., this is from It can be seen in the following example. After the thread is awakened, it will retrieve whether the judgment is satisfied, if it is not satisfied, generally speaking that the thread should still block it here, waiting for being awakened next time. This process generally implements the WHILE statement. Another function used to block the thread is pthread_cond_timedwait (), and its prototype is:

EXTERN INT PTHREAD_COND_TIMEDWAIT __P ((pthread_cond_t * __C,

PTHREAD_MUTEX_T * __ MUTEX, __CONST STRUCT TIMESPEC * __ ABSTIME));

It is more than a time parameter than the function pthread_cond_wait (), and after the Abstime segment is not satisfied, the block is released.

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 COND of the condition variable. When multiple threads are blocked on this condition variable, which thread is awakened is determined by the scheduling policy of the thread. It should be noted that this function must be protected with a mutex lock of the condition variable, otherwise the condition satisfies the signal and the test condition and call the pthread_cond_wait function, resulting in unlimited waiting. Below is a simple example of using functions pthread_cond_wait () and functions 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 opens the mutex count_lock. At this time, when the function increment_count is called, the PTHREAD_COND_SIGNAL () function changes the condition variable to inform Decrement_count () stop blocking. The reader can try two threads running these two functions, see what kind of result will occur.

Function pthread_cond_broadcast (pthread_cond_t * Cond) is used to wake all threads that are blocked on the conditional variable COND. These threads will be woken up and will compete again to compete again, so this function must be carefully used. 4.4 semaphore

The amount of signal is essentially a non-negative integer counter that is used to control access to public resources. When the public resource is increased, the call function SEM_POST () increases the amount of semaphore. Only public resources can be used when the signal value is greater than 0, and the function SEM_WAIT () can be used. Function SEM_TRYWAIT () and functions PTHREAD_ MUTEX_TRYLOCK () the same role, it is a non-blocking version of function SEM_WAIT (). Below we introduce some of the functions related to the quantity by one by one, they are defined in header file /usr/include/semaphore.h.

The data type of the semaphore is structural SEM_T, which is essentially a long integer. 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 the semaphore; when PShared is not 0, this signal is shared between processes, otherwise it can only share all threads of the current process; VALUE gives the initial value of the signal.

Function SEM_POST (SEM_T * SEM) is used to increase the value of the signal. When a thread is blocked on this session, calling this function will make one of the threads are not blocked, and the selection mechanism is also determined by the scheduling policy of the thread.

Function SEM_WAIT (SEM_T * SEM) is used to block the current thread until the value of the Sem SEM is greater than 0, and the value of the SEM is reduced after the block is released, indicating that the public resource is reduced. Function SEM_TRYWAIT is a non-blocking version of function SEM_WAIT (), which minimizes the value of Sem Sem.

Function SEM_DESTROY (SEM_T * SEM) is used to release semapons SEM.

Let's take a look at an example of using a semaphore. In this example, there are 4 threads, two threads are responsible for reading data from the file to the common buffer, and the other two threads read data from the buffer (add-to-pending).

/ * File sem.c * /

#include

#include

#include

#define maxStack 100

INT stack [maxstack] [2];

INT size = 0;

SEM_T SEM;

/ * Read the data from the file 1.DAT, each read once, the semaphore is added * /

Void readdata1 (void) {

File * fp = fopen ("1.dat", "r");

While (! feof (fp)) {

FSCANF (FP, "% D% D", & Stack [Size] [0], & Stack [Size] [1]);

SEM_POST (& SEM);

size;

}

Fclose (fp);

}

/ * Read data from file 2.DAT * /

Void readdata2 (void) {

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

While (! feof (fp)) {

FSCANF (FP, "% D% D", & Stack [Size] [0], & Stack [Size] [1]); SEM_POST (& SEM);

size;

}

Fclose (fp);

}

/ * Blocking waiting buffer has data, read data, release space, continue waiting * /

Void Handledata1 (void) {

While (1) {

SEM_WAIT (& SEM);

Printf ("Plus:% D % D =% D / N", Stack [Size] [0], Stack [Size] [1],

Stack [Size] [0] Stack [Size] [1]);

--Size;

}

}

Void HandleData2 (void) {

While (1) {

SEM_WAIT (& SEM);

Printf ("Multiply:% D *% D =% D / N", stack [size] [0], stack [size] [1],

Stack [Size] [0] * stack [size] [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);

/ * Prevent the program from exadring out, let it wait for this indefinite period * /

Pthread_join (T1, NULL);

}

Under Linux, we generate an executable SEM with the command gcc -lpthread sem.c -o sem. We edit data files in advance 1.DAT and 2.Dat, assuming their contents are 1 2 3 4 5 6 7 8 9 10 and -1 -2 -3 -4 -5 -6 -7 -8 -9 - 10, we run SEM to 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

We can see the competitive relationship between each thread. The value does not appear in our original order this is because the value of Size is revised by each thread. This is often a problem that multi-threaded programming should pay attention.

5 small knot

Multi-threaded programming is a very interesting technology that uses multithreaded technology network ant is one of the most commonly used download tools, using multi-threaded technology for a few times more than single-threaded GREP, similar example There are a lot more. I hope everyone can write efficient and practical programs with multi-threaded technology.

转载请注明原文地址:https://www.9cbs.com/read-41447.html

New Post(0)