By the way today, there are some considerations for debugging and testing when developing C + + multithreaded applications. The following considerations are mainly for C + +, but some are appropriate for other languages as well.
About setting breakpoints and stepping
Many students are very dependent on the debugger's breakpoint function and Single-step function. This is fine in a single-threaded scenario (though some single-threaded but GUI-related programs can be a bit cumbersome). As for the debugging of multithreaded routines, these two methods are simply the beginning of a nightmare. The main problems caused by multithreading are mostly related to the race conditions (Race Condition, detailed explanation of "here"). setting breakpoints or stepping traces can severely interfere with the competitive state between multiple threads. Cause what you see is an illusion. For example, there are two threads executing concurrently, and there are some discordant bugs (caused by the race state). Once you set a breakpoint on a thread, the thread stops at the break point, leaving only another thread running. At this time, the concurrent scene has been completely destroyed, you can see through the debugger is a harmonious scene.
Run a little bit of the problem. This is similar to the "uncertainty principle" of quantum mechanics, where the observer's behavior interferes with the object being measured, causing the observer to see a disturbing phenomenon.
About log output
Since breakpoints and single-step are not good to use. What about the pinch? An alternative is to output log logs. It can effectively mitigate side effects caused by breakpoints and single-step (for race conditions).
The problem of traditional log mechanism
The traditional log output is mainly printed to the screen or output to the file. For C + +, classes and functions built into the standard library (such as cout, printf, fputs) may have problems with thread security (related to the compiler's specific implementation). In particular, the standard Stream class library (iostream) of the eight global objects, but also be cautious to use. The light output is mixed with log text, and heavy causes the program to crash.
In view of the above reasons, we should try to use the third party line threading built-in log mechanism to handle log output function. such as Ace built-in Ace_log_msg and so on.
The log function should be short and dapper
In many cases, we will wrap a common function to achieve the log output function. The Log class/function of the line threading is then called inside the function. In order not to affect the thread's race condition, the log function is as simple and lightweight as possible: don't involve too much assorted trivia, don't take time-consuming actions, try not to manipulate some global variables.
Side effects of log
But pinch, even if the log function is short and concise, it is still possible to affect the race conditions (after all, log has overhead, but also to consume CPU time).
If the race condition is affected by log, it is tricky. I have had this situation before: Add log, program no problem, remove log, the program randomly crashes. There are generally two possibilities for this: either the log function itself is problematic or the program's race conditions are very sensitive (even the cost of the log will be affected).
The only thing you can rely on is the naked eye and the human brain. First look at the relevant code and document carefully several times (it is best to find other experienced people together with code Review), and then we all use their brains to ponder.
About the debug and release versions
C + + programs often have the difference between the debug version and release version. Sometimes, this can lead to some multi-threaded problems.
Because the debug version contains some debugging information, some debugging mechanisms are enabled (such as Assert macros). So it may affect the competitive state of multithreading. In the unfortunate time, will run into the debug version of the normal work, release version of the program randomly crashes. To avoid this situation, consider the following two options:
Discard using the Debug version
You can simply give up using the debug version. In this case, you need to consider replacing a macro-related debugging such as Assert with your own set of macros so that it can take effect under the non-debug version.
Two versions of sync test
Using this method, a programmer can use the debug version at a normal self-test, but the tester must be release version of the test. Specific steps can be made using a daily build to assist (see "Here" for a daily build). Be sure to avoid: In peacetime only debug version of the test, wait until the release on the eve of production release version. This is a very dangerous thing to do!
About testing the machine (hardware)
Say a personal experience, an impressive thing.
When using ACE to develop Cross-platform programs, the company's development environment and test environment are single CPU machines. Because the multi-core machine was not available at that time, many CPUs of the machine is very expensive, the company is not willing to spend money allocation.
After the software was developed, the testers passed several rounds of regression tests and found no significant problems. But running in a client's environment often crashes randomly. Since it is not possible to debug in a customer environment and live without problems, several developers in the development group had to give full play to the visual and human brain functions (staring at code and designing documents). After n long time, almost the head to break, finally realized that the client's machine is more than CPU. Then hurriedly borrowed from other departments a multiple CPU machine, installed software debugging, finally found that a third-party library has problems. After this, I immediately came up with a variety of ways to apply for a number of CPU machines for testers to use.
As a result of the above, I strongly recommend that if you are developing multi-threaded applications, try to give every programmer and tester a multi-core/multi CPU machine. After all, multi-core machines are already popular, even if the CPU machines, the price is also good. There is no need to introduce development risks (not just waste the development/tester time, but also the cost of implementation and maintenance) for the sake of saving that little penny.
In addition, there may be a reunion asking "How does a hyper-threading machine pinch?" About the difference between multiple CPUs, multi-core and Hyper-threading, interested students can see "here". I personally feel that hyper-threading is not as good as multi-core and multiple CPUs.