Overview
In general, a log is required in an application to record the state of the program's operation so that later problems can be traced. In the design of the journal system, there is usually a general log system to harmonize the settings of these logs such as location, output level and content. In multithreaded programming, when each thread needs to output a log, the design of the log system is more complex because of the synchronization between threads.
In a single-threaded application, it is common to use a single log case to apply important log information from a file to the runtime, but in a multithreaded environment, this is obviously not a good idea, because the logs printed by each thread are complex, making the log files difficult to read and track. The better way is to record the main thread of their own log, individual child threads record their own log. In order to keep the simple and clear characteristic of the log system in the multithreaded environment, this paper uses the thread local variable to implement the log system under multithreading. It ensures that each thread has its own clear log file, each thread has only a simple log case, so that the log system has a simple and clear and efficient features and is applicable to single-threaded and multi-threaded applications.
The multithread log system described in this paper is a realization based on C + + and Boost library, which is very useful for the implementation of the log system in multithread environment. If the original design of the log system does not consider the multithreaded environment, as the development of the business needs to implement multithreading, this method can easily transform the existing log system, so as to achieve multi-threaded logging system, and this method can keep the original log system business interface unchanged.
Background information
For the concept of thread-local storage, as literally, each variable has a separate copy in each thread. By using thread-local storage technology, you can avoid synchronization problems between threads, and different threads can use different log settings. By using the smart pointer boost::thread_specific_ptr of the Boost library to access thread-local storage, each thread needs to initialize the smart pointer when it first attempts to get an instance of it, and the thread-locally stored data is freed by the Boost library when it exits.
The advantages of multithreaded logging using thread-local variables:
It is easy to implement a thread-level single log system using static thread-local variables.
It is simple and convenient to access the generation and cleanup of thread local storage through the intelligent pointer boost::thread_specific_ptr;
The use of thread local variables makes it easy to implement multithreading support for an existing single instance log system without altering any original log interface;
Single-Thread environment log
In general, the log class is implemented as a single case, making it easy to invoke. For simplicity, the write operation of the log code in the example is printed directly to the console. The following is the initial definition of the Logger class:
Listing 1. The initial definition of the Logger class
Class Logger
{
private:
Logger () {} public
:
static void Init (const std::string &name);
Static Logger *getinstance ();
void Write (const char *format, ...);
Private:
static std::string ms_name;
Static Logger *ms_this_logger;
The Init () function is used to set the name of the Logger. In practical applications, you can set up configuration information for other Logger. In a single-threaded environment, you can write a log every time you call the Write () function.
Logging of multithreaded environments
Multi-threaded environment to implement the log system, you must lock the write operation, otherwise it will get chaotic output. An easy way to think of this is to maintain a list in the Logger class, store instances of Logger in all threads by name, and find each thread by name and use the thread's own unique Logger.
This is a bit like the implementation of log4j, the difference is that log4j all the Logger configuration in the configuration file, each Logger has a separate configuration. But our Logger cannot implement this function because the configuration information is passed in at runtime and all Logger share the same configuration information.
Another big problem is that you have to modify the declaration of getinstance () and add a parameter similar to the Logger name. This practice destroys the original API, and the existing code must be completely modified to support the new Logger class.
With thread-local storage, you can resolve the above two issues: each Logger configuration is thread independent and does not require modification of the getinstance () declaration. Here's the new Logger class declaration, which uses the boost's Thread_specific_ptr class, which implements a cross-platform, thread-local storage solution.
Listing 2. Logger class definition using thread-local storage
Class Logger
{
private:
Logger () {} public
:
static void Init (const std::string &name);
Static Logger *getinstance ();
void Write (const char *format, ...);
Private:
static boost::thread_specific_ptr<std::string> ms_name;
Static boost::thread_specific_ptr<logger> Ms_this_logger;
The simple use of the Boost::thread_specific_ptr class in your code declares two static variables in the class, which are put into thread-local storage at run time.
Listing 3. Implementation of the Logger class using thread local variables
void Logger::init (const string &name)
{
if (!name.empty ()) {
Ms_name.reset (new std::string (name));
}
Logger *logger::getinstance ()
{
if (ms_this_logger.get () = NULL) {
ms_this_logger.reset (new Logger);
} return
ms_this_logger.get ();
}