LINUX multi-thread design details!

Source: Internet
Author: User
LINUX multi-thread design

1 Introduction
The thread technology was proposed as early as 1960s, but the actual application of multithreading to the operating system was 80 years ago.
In the middle of the year, Solaris was a leader in this field. Traditional UNIX also supports the concept of thread, but only one thread is allowed in a process, which means multiple threads
Process. Now, multithreading technology has been supported by many operating systems, including Windows/NT and, of course, Linux.
Why do we need to introduce threads after the concept of a process is introduced? What are the advantages of multithreading? What systems should adopt multithreading? We must answer these questions first.
One of the reasons for using multithreading is that compared with processes, it is a very "frugal" multi-task operation method. We know that
In Linux, when a new process is started, it must be assigned an independent address space, and a large number of data tables are created to maintain its code segment, stack segment, and data segment, this is an "expensive" multitasking Operator
. While multiple threads running in a process use the same address space for each other to share most of the data. The space required to start a thread is much less than the space required to start a process, while
In addition, the time required for switching between threads is much less than the time required for switching between processes. According to statistics, the overhead of a process is about 30 times the overhead of a thread.
This data may have a big difference.
The second reason for using multithreading is the convenient communication mechanism between threads. For different processes, they have independent data space.
Transmission of row data can only be performed through communication. This method is not only time-consuming, but also inconvenient. The thread does not, because the threads in the same process share data space, the data of a thread can be directly
Used by other threads, which is fast and convenient. Of course, data sharing also brings about other problems. Some Variables cannot be modified by two threads at the same time, and some subroutines declare static data.
It is more likely to bring a catastrophic blow to the multi-threaded program, which is the most important thing to pay attention to when writing multi-threaded programs.
In addition to the advantages mentioned above, multi-threaded programs, as a multi-task and concurrent working method, certainly have the following advantages:
1) Improve application response. This is especially meaningful for graphic interface programs. When an operation takes a long time, the entire system will wait for this operation. At this time, the program will not respond to keyboard, mouse, and menu operations, but will use multithreading technology, putting time consuming in a new thread can avoid this embarrassing situation.
2) Make the multi-CPU system more effective. The operating system ensures that different threads run on different CPUs when the number of threads is not greater than the number of CPUs.
3) Improve the program structure. A long and complex process can be considered to be divided into multiple threads and become several independent or semi-independent running parts. Such a program will facilitate understanding and modification.
Next we will try to write a simple multi-threaded program.
2. Simple multi-thread programming
Multithreading in Linux follows the POSIX thread interface, which is called pthread. Write multiple
The header file pthread. H is required for the thread program, and the library libpthread. A is required for connection. By the way, in Linux, the implementation of pthread is through the system call
Clone. Clone () is a Linux-specific system call. It is used in a similar way as fork. For details about clone (), interested readers can query
See the relevant documentation. The following is a simple multi-threaded program example1.c.
/* Example. 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
Run example1 and 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 and we may get the following results:
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 two results are different, which is the result of two threads competing for CPU resources. In the above example, we used two functions, pthread_create and pthread_join, and declared a variable of the pthread_t type.
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 pthread_create function is used to create a thread. 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 the pointer to the thread identifier, the second parameter is used to set the thread attribute, and the third parameter is
Start address. The last parameter is the parameter for running the function. Here, our function thread does not need parameters, so the last parameter is set as a null pointer. We also set the second parameter as a null pointer.
The thread with the default attribute. The setting and modification of thread attributes will be described in the next section. When the thread is successfully created, the function returns 0. If the value is not 0, the thread creation fails. The common error code returned is
Eagain and einval. The former indicates that the system restricts the creation of new threads. For example, the number of threads is too large. The latter indicates that the second parameter indicates that the thread attribute value is invalid. After the thread is successfully created, the new line
Run the function with parameters 3 and 4, and the original thread continues to run the next line of code.
The pthread_join function is used to wait for the end of a thread. Function prototype:
Extern int pthread_join _ p (pthread_t _ th, void ** _ thread_return ));
The first parameter is the identifier of the waiting thread, and the second parameter is a user-defined pointer, which can be used to store the waiting thread
. This function is a thread-blocking function. The function called will wait until the end of the waiting thread. When the function returns, the resources of the waiting thread will be reclaimed. There are two ways to end a thread:
One way is that the function ends and the thread that calls it ends, as in the preceding example. The other way is through the function pthread_exit. Its function prototype is:
Extern void pthread_exit _ p (void * _ retval) _ attribute _ (_ noreturn __));
The only parameter is the return code of the function, as long as the second parameter in pthread_join
Thread_return is not null. This value will be passed to thread_return. The last note is that a thread cannot be waited by multiple threads; otherwise, the first thread receives
The signal thread returns success, and the other threads that call pthread_join return the error code esrch.
In this section, we write a simple thread and master the three most commonly used functions pthread_create, pthread_join, and pthread_exit. Next, let's take a look at some common attributes of the thread and how to set these attributes.
3. modify attributes of a thread
In the example in the previous section, we used the pthread_create function to create a thread. In this thread, we used 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/usr/include
As defined in/pthread. H, people who like to follow the root of the problem can view it on their own. Attribute values cannot be set directly. Related 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, and stack size.
Small, priority. The default attributes are non-bound, non-separated, 1 MB stacks by default, and have the same priority as the parent process.
Thread binding involves another concept: Light process (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
Fixed "binding" on a lightweight process. The bound thread has a high response speed. This is because the CPU time slice is scheduled for light processes. The bound thread can always have
Lightweight processes are available. 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 a pointer to the property 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.
# Include <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 );
The separation status of a thread determines how a thread terminates itself. In the above example, we use the default thread
In this case, the original thread waits for the end of the created thread. Only when the pthread_join () function returns, the created thread is terminated.
System resources used. The separation thread is not like this. It is not waiting by other threads. When the running ends, the thread is terminated and system resources are released immediately. Programmers should choose
Proper separation status. 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 (separation thread) and pthread.
_ Create_joinable (non-detached thread ). Note that if you set a thread as a separate thread and the thread runs very fast, it is likely
The pthread_create function is terminated before it returns the result. After it is terminated, it may hand over the thread number and system resources to other threads for use. In this way, the pthread_create
The thread gets the wrong thread number. To avoid this situation, you can take some synchronization measures. One of the simplest ways is to call
The pthread_cond_timewait function allows this thread to wait for a while and leave enough time for the function pthread_create to return. Set a wait time, yes
Common Methods in multi-threaded programming. However, do not use functions such as wait (), which sleep the entire process and cannot solve the thread synchronization problem.
Another common attribute is the thread priority, which is stored in the schema sched_param. Functions
Pthread_attr_getschedparam and the pthread_attr_setschedparam function are stored. Generally, we always take the optimization first.
Level 1: Modify the obtained value before storing it 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 );
4. Thread Data Processing
Compared with processes, one of the biggest advantages of threads is data sharing. Each process shares the data segment of the parent process, which is convenient.
To 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 functions 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 in the function
When a thread calls the function to obtain the address and points to the data using the address, other threads may call the function and modify the data. Shared variables must be determined by the keyword volatile.
To prevent the compiler from using the-ox parameter during optimization (for example, in GCC. To protect variables, we must use semaphores, mutex, and other methods to ensure that the variables are correct.
. Next, we will gradually introduce the relevant knowledge when processing thread data.
4.1 thread data
In a single-threaded program, there are two basic types of data: global variables and local variables. However, there is a third type of data in multi-threaded programs.
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. The necessity of such data is obvious.
. 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 output in thread a is probably the error message of line B. 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 indicate thread data. However, 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:
Extern int pthread_key_create _ p (pthread_key_t * _ key,
Void (* _ destr_function) (void *)));
The first parameter is a pointer to a key value, and the second parameter specifies a destructor function.
When each thread ends, the system calls this function to release the memory block bound to this key. This function is often used with the pthread_once function.
(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
Will be ignored in future calls.
In the following example, we create a key and associate it with a certain data. Define a function.
Createwindow, which defines a graphical window (Data Type: 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_setspecific (mywinkey, Win );
}
/* The createmykey function creates a key and specifies the Destructor */
Void createmykey (void ){
Pthread_key_create (& mywinkey, freewinkey );
}
/* Function freewinkey, release space */
Void freewinkey (fl_window * Win ){
Delete win;
}
In this way, you can call the createmywin function in different threads to obtain window changes 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 set the thread
Data is bound with a key. The two functions are prototype 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 pthread_setspecpacific specifies a new thread data for a key, 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. However, it should also be noted that it only releases the memory occupied by the key and does not release the thread data associated with this key.
The memory used, 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.

4.2 mutex lock
Mutex lock is used to ensure that only one thread is executing a piece of code within a period of time. Necessity is obvious: assuming that each thread writes data to the same file in sequence, the final result must be disastrous.
Let's take a look at the following code. This is a read/write program. They share a buffer zone, and we assume that a buffer zone can only save one piece of information. That is, the buffer has only two States: 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 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 mutex variable mutex is declared here, and the structure pthread_mutex_t is an undisclosed data type.
Contains a property object allocated by the system. The pthread_mutex_init function is used to generate a mutex lock. The null parameter indicates that the default attribute is used. If you need to declare the interaction of specific attributes
The pthread_mutexattr_init function must be called. Functions pthread_mutexattr_setpshared and functions
Pthread_mutexattr_settype is used to set the mutex lock attribute. The previous function sets the property pshared, which has two
Value: pthread_process_private and pthread_process_shared. The former is used for Thread Synchronization in different processes, and the latter for local Synchronization
Different threads. In the above example, we use the default property pthread_process _
Private. The latter is used to set mutex lock types. The 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.
The pthread_mutex_lock statement begins to lock with a mutex lock, and the subsequent code is called
Pthread_mutex_unlock is locked, that is, only one thread can call and execute at a time. When a thread is executed at pthread_mutex_lock
If the lock is used by another thread at this time, the thread is blocked, that is, the program will wait for another thread to release the mutex lock. In the above example, we use
The pthread_delay_np function enables a thread to sleep for a period of time to prevent a thread from occupying this function.
The above example is very simple and I will not introduce it any more. It is suggested that a deadlock may occur when mutex lock is used:
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. 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 the deadlock inevitable, it will return the corresponding information, the programmer can make corresponding processing for the deadlock. In addition, different mutex lock types have different deadlocks, but the most important thing is that programmers should
Pay attention to this in programming.
4.3 condition Variables
The previous section describes how to use mutex to share and communicate data between threads. An obvious disadvantage of mutex is that it only
There are two statuses: Locked and unlocked. 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 used, the 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
A thread that is blocked by this condition variable. These threads will re-lock the mutex and re-test whether the conditions are met. Generally, condition variables are used for synchronization between threads.
The condition variable structure is pthread_cond_t. The pthread_cond_init () function 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 ));
Cond is a pointer to the structure pthread_cond_t, and cond_attr is a pointer
Pointer to the pthread_condattr_t structure. The structure pthread_condattr_t is the attribute structure of the condition variable. We can use it to set items like mutex locks.
Whether the component 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. Release a condition variable
The function is pthread_cond _ destroy (pthread_cond_t Cond ).
The pthread_cond_wait () function blocks the thread on a condition variable. Its function prototype is:
Extern int pthread_cond_wait _ p (pthread_cond_t * _ cond,
Pthread_mutex_t * _ mutex ));
The thread unlocks the lock pointed to by mutex and is blocked by the condition variable cond. The thread can be called by a function.
Pthread_cond_signal and the pthread_cond_broadcast functions wake up. However, note that the condition variables only start to block and wake up threads.
For example, whether a variable is 0 or not. We can see this in the following example. After the thread is awakened, it checks again to determine whether the conditions are met.
Yes. Generally, the thread should still be blocked and be waiting for the next wake-up. This process is generally implemented using the while statement.
Another function used to block threads is pthread_cond_timedwait (). Its prototype is:
Extern int pthread_cond_timedwait _ p (pthread_cond_t * _ cond,
Pthread_mutex_t * _ mutex, _ const struct timespec * _ abstime ));
It has one more time parameter than the pthread_cond_wait () function. After a period of time in abstime, blocking is also removed even if the condition variable is not met.
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. Which thread is blocked when multiple threads are blocked on this condition variable?
Wake-up is determined by the thread scheduling policy. It must be noted that the mutex lock of the Protection Condition variable must be used to protect this function. Otherwise, the condition satisfies the signal and may be called in the test conditions.
The pthread_cond_wait function is issued between them, resulting in unlimited waiting. The following uses the pthread_cond_wait () and Function
A simple example of 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 enable the mutex lock count_lock. In this case, when the increment_count function is called, The pthread_cond_signal () function changes the condition
To inform decrement_count () to stop blocking. Readers can try to let the two threads run the two functions separately to see what results will appear.
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.
4.4 semaphores
Semaphores are essentially non-negative integer counters used to control access to public resources. When public resources increase
The sem_post () function adds a semaphore. Public resources can be used only when the signal value is greater than 0. after use, the sem_wait () function reduces semaphores. Function
Sem_trywait () and function pthread _
Mutex_trylock () plays the same role. It is a non-blocking version of The sem_wait () function. Next we will introduce some functions related to semaphores one by one.
/Usr/include/semaphore. h.
The data type of the semaphore is sem_t, which is essentially a long integer. The sem_init () function 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 structure. If pshared is not 0, the semaphore is shared among processes. Otherwise, it can only be shared among all threads of the current process. value indicates the initial value of the semaphore.
The sem_post (sem_t * SEM) function is used to increase the semaphore value. When a thread is blocked on this semaphore, calling this function will make one of the threads not blocked. The selection mechanism is also determined by the thread scheduling policy.
Function sem_wait (sem_t * SEM
) Is used to block the current thread until the semaphores SEM value is greater than 0. After blocking is removed, the SEM value is reduced by one, indicating that the public resources are reduced after use. Function sem_trywait (
Sem_t * SEM) is a non-blocking version of The sem_wait () function, which directly reduces the semaphores SEM value by one.
The sem_destroy (sem_t * SEM) function is used to release semaphores.
Here is an example of using semaphores. In this example, there are a total of four threads, two of which are responsible for reading data from the file to the public buffer, the other two threads read data from the buffer for different processing (addition and multiplication ).
/* File Sem. C */
# Include <stdio. h>
# Include <pthread. h>
# Include <semaphore. h>
# Define Max stack 100
Int stack [maxstack] [2];
Int size = 0;
Sem_t SEM;
/* Read data from the file 1. dat. Each time it is read, the semaphore is incremented by one */
Void readdata1 (void ){
File * fp = fopen ("1.dat"," R ");
While (! Feof (FP )){
Fscanf (FP, "% d", & stack [0], & stack [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", & stack [0], & stack [1]);
Sem_post (& SEM );
++ Size;
}
Fclose (FP );
}
/* Block wait for the buffer to have data. After reading the data, release the space and continue waiting */
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 );
/* Prevent the program from exiting too early and keep it waiting for an indefinite period of time */
Pthread_join (T1, null );
}
In Linux, we use the command gcc-lpthread Sem. C-o
SEM generates the executable file SEM. We have edited the data files 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, run SEM and obtain 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 competition between threads. The value is not displayed in the original order because the value of size is randomly modified by various threads. This is often a problem that needs to be paid attention to in multi-threaded programming.
5 Summary
Multi-threaded programming is a very interesting and useful technology. Network ant financial, which uses multithreading technology, is one of the most commonly used download tools, the grep using multithreading technology is several times faster than the single-thread grep. There are many other similar examples. I hope you can use multithreading technology to write efficient and practical programs.

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.