Several points needing attention in multithreading programming

Source: Internet
Author: User
Tags exception handling volatile
1th Chapter The synchronization of the user mode thread
1. Only one statement uses a problem that does not consider thread synchronization.
When programming in a high-level language, we tend to assume that a statement is the smallest atomic access, and the CPU does not run other threads in the middle of the statement. This is wrong, because even a very simple statement of a high-level language, compiled by the compiler, can become multiple lines of code executed by the computer. Therefore, you must consider the issue of thread synchronization. No thread should modify a shared variable by invoking a simple C statement.
2. interlock functions have those.
(1) long InterlockedExchangeAdd (Lplong addend, long Increment);
Addend is the address of a long integer variable, increment the value that you want to add to the long variable that the addend points to (can be negative). The primary role of this function is to ensure that this Garco is accessed as an atom.
(2) long InterlockedExchange (Lplong Target, long Value);
Replaces the value pointed to by the first argument with the value of the second argument. The function return value is the original value.
(3) pvoid InterlockedExchangePointer (pvoid *target, pvoid Value);
Replaces the value pointed to by the first argument with the value of the second argument. The function return value is the original value.
(4) LONG InterlockedCompareExchange (
Lplong destination, long Exchange, long Comperand);
If the third argument has the same value as the first argument, the second argument replaces the value pointed to by the first argument. The function return value is the original value.
(5) Pvoid Interlockedcompareexchangepointer (
Pvoid *destination, pvoid Exchange, pvoid Comperand);
If the third argument has the same value as the first argument, the second argument replaces the value pointed to by the first argument. The function return value is the original value.
3. Why a single CPU computer should not use a looping lock.
An example is provided:
BOOL g_bresourceuse = FALSE;
......
void ThreadFunc1 ()
{
BOOL bresourceuse = FALSE;
while (1)
{
Bresourceuse = InterlockedExchange (&g_bresourceuse, TRUE);
if (Bresourceuse = = FALSE)
{
Break
}
Sleep (0);
}
......
......
......
InterlockedExchange (&g_bresourceuse, FALSE);
}
Looping locks first wastes CPU time. The CPU must continuously compare two values until one value is "magically" changed by another thread. Also, threads that use the loop lock should be of the same priority and should use the Setprocesspriorityboost function or the SetThreadPriorityBoost function to prevent the dynamic enhancement of thread precedence, Otherwise, a lower-priority thread may never be invoked.
4. How to declare variables using volatile.
If you are using the address of a shared resource such as &g_resource then you can use volatile, because when you pass a variable address to a function, the function must read the value from memory. The optimizer does not have any effect on it. If you use a variable directly, you must have a qualifier for the volatile type. It tells the compiler that variables can be modified by something other than the application itself, including the operating system, hardware, or concurrently executing threads. The volatile qualifier tells the compiler not to optimize the variable and always reload the value of the memory cell from the variable. Otherwise, the compiler will put the value of the variable into the CPU register, each time the register to operate. The thread goes into an infinite loop and never wakes up.
5. How to implement synchronization of threads using critical code snippets.
If a small piece of code is required to execute in an atomic manner, the simple interlock function is not enough, and the critical code segment must be used to solve the problem. However, when using critical code snippets, it is easy to fall into a deadlock state because the timeout value cannot be set while waiting to enter critical code segments. Critical code Snippets are implemented by setting a flag for shared resources, just like the "someone/No Man" sign on the toilet door. This symbol is a critical_section variable. The variable should be initialized before any one of the threads uses it. There are two ways to initialize, using the InitializeCriticalSection function and the InitializeCriticalSectionAndSpinCount function. Then use the EnterCriticalSection function or use the TryEnterCriticalSection function before each critical code snippet that uses the shared resource's thread function. Call the LeaveCriticalSection function after the critical code snippet is used. After all threads are no longer using the shared resource, you should call the DeleteCriticalSection function to clear the flag. An example is provided:
const int max_times = 1000;
int g_intindex = 0;
DWORD G_dwtimes[max_times];
Critical_section G_cs;

void Init ()
{
......
InitializeCriticalSection (&g_cs);
......
}

DWORD WINAPI Firstthread (pvoid Lpparam)
{
while (G_intindex < max_times)
{
EnterCriticalSection (&g_cs);
G_dwtimes[g_intindex] = GetTickCount ();
g_intindex++;
LeaveCriticalSection (&g_cs);
}
return 0;
}

DWORD WINAPI Secondthread (pvoid Lpparam)
{
while (G_intindex < max_times)
{
EnterCriticalSection (&g_cs);
g_intindex++;
G_dwtimes[g_intindex-1] = GetTickCount ();
LeaveCriticalSection (&g_cs);
}
return 0;
}

void Close ()
{
......
DeleteCriticalSection (&g_cs);
......
}
Some tips for using critical code snippets should be noted:
(1) Each shared resource uses a critical_section variable.
This allows another resource to be owned by another thread when the current thread occupies one resource.
EnterCriticalSection (&g_cs);
for (intloop = 0;  Intloop < 100; intloop++)
{
G_intarray[intloop] = 0;
G_uintarray[intloop] = 0;
}
LeaveCriticalSection (&g_cs);
To
EnterCriticalSection (&g_csint);
for (intloop = 0;  Intloop < 100; intloop++)
{
G_intarray[intloop] = 0;
}
LeaveCriticalSection (&g_csint);
EnterCriticalSection (&g_csuint);
for (intloop = 0;  Intloop < 100; intloop++)
{
G_uintarray[intloop] = 0;
}
LeaveCriticalSection (&g_csuint);
(2) To access multiple resources at the same time, access to the resource must always be requested in exactly the same order.
This will prevent the deadlock state from being generated. The order of departure is not related.
Thread1:
EnterCriticalSection (&g_csint);
EnterCriticalSection (&g_csuint);
for (intloop = 0;  Intloop < 100; intloop++)
{
G_uintarray[intloop] = G_intarray[intloop];
}
LeaveCriticalSection (&g_csint);
LeaveCriticalSection (&g_csuint);
Thread2:
EnterCriticalSection (&g_csuint);
EnterCriticalSection (&g_csint);
for (intloop = 0;  Intloop < 100; intloop++)
{
G_uintarray[intloop] = G_intarray[intloop];
}
LeaveCriticalSection (&g_csint);
LeaveCriticalSection (&g_csuint);
To
Thread1:
EnterCriticalSection (&g_csint);
EnterCriticalSection (&g_csuint);
for (intloop = 0;  Intloop < 100; intloop++)
{
G_uintarray[intloop] = G_intarray[intloop];
}
LeaveCriticalSection (&g_csint);
LeaveCriticalSection (&g_csuint);
Thread2:
EnterCriticalSection (&g_csint);
EnterCriticalSection (&g_csuint);
for (intloop = 0;  Intloop < 100; intloop++)
{
G_uintarray[intloop] = G_intarray[intloop];
}
LeaveCriticalSection (&g_csint);
LeaveCriticalSection (&g_csuint);
(3) Do not run the critical code segment for a long time.
EnterCriticalSection (&g_cs);
SendMessage (HWnd, wm_somemsg, &g_s, 0);
LeaveCriticalSection (&g_cs);
To
EnterCriticalSection (&g_cs);
Stemp = g_s;
LeaveCriticalSection (&g_cs);
SendMessage (HWnd, wm_somemsg, &stemp, 0);
6.initializecriticalsection/initializecriticalsectionandspincount difference.
The return value of the InitializeCriticalSection function is empty and the event kernel object is not created, which saves system resources, but when two or more threads are competing for critical code snippets, critical code snippets may be contention if there is not enough memory, Also, the system may not be able to create the necessary event kernel objects. The EnterCriticalSection function will then produce a Exception_invalid_handle exception. This error is very rare. If you want to be prepared for this situation, there are two options. You can use structured exception handling methods to track errors. When an error occurs, you can either not access the resources protected by the critical code snippet, or wait for some memory to become available, and then call the EnterCriticalSection function again.
Another option is to use InitializeCriticalSectionAndSpinCount, the second parameter dwspincount, to pass the number of times you want to loop the lock loop iteration before the thread waits for it to acquire a resource. This value can be any number between 0 and 0X00FFFFFF. If the function is called when run on a single-processor computer, the parameter is ignored and is always set to 0. Use the InitializeCriticalSectionAndSpinCount function to create critical code snippets to ensure that the high information bits of the dwSpinCount parameter are set. When the function discovers that the high information bit has been set, it creates the event kernel object and associates it with the critical code snippet when it is initialized. If the event cannot be created, the function returns FALSE. This event in the code can be handled more appropriately. If the event is created successfully, EnterCriticalSection will always be able to run and never produce an exception (if the event kernel object is always allocated beforehand, the system resources will be wasted.) The event kernel object can be overallocated only if the code does not allow the entercriticalsection to fail, or if there is a contention, or if the process is expected to run in a very memory-starved environment.
What is the difference between 7.TryEnterCriticalSection and entercriticalsection?
If EnterCriticalSection puts a thread in the waiting state, the thread cannot be scheduled again for a long time. In fact, in poorly written applications, the thread will never be given CPU time again. The TryEnterCriticalSection function will never allow the calling thread to enter the wait state. Its return value can indicate whether the calling thread can gain access to the resource. TryEnterCriticalSection finds that the resource is already accessed by another thread, it returns false. In all other cases, it returns true. Using this function, a thread can quickly see if it can access a shared resource, and if it cannot, it can continue to perform some other operation without waiting. If the TryEnterCriticalSection function does return True, then the critical_section member variable has been updated. Windows98 There is no implementation code for the TryEnterCriticalSection function that can be used.


You are welcome to use http://Blogmove.cn to provide "blog move" and "Bowen Three caves" service.

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.