[All at once] thread synchronization of log threads

Source: Internet
Author: User

In a very small ApplicationProgramIn, I want to add the log recording function. I have assisted in analyzing and judging the causes of some events, such as logon denied. Because this applet is a dialog box program, it basically runs in the memory, and logging requires frequent file opening and closing, file writing, from the "conscious" feeling, I am a little worried that logging will affect the running efficiency. Therefore, in order not to affect the response performance of the UI thread, I decided to create a separate thread dedicated to this, called it a "log thread ".

At the same time, in order to reduce the frequency of file access, I placed a log buffer in the memory (static zone). When writing logs, I first added the content to the log buffer, then, the UI thread regularly notifies the log thread to output the buffer content to the file. That is, the following model is used:

 

[UI thread] ---> | log buffer | <--- [log thread]

|

| -------------> Log File]

 

We start a new thread through createthread. When we need to output a buffer, we send a custom message to the thread through the postthreadmessage method, telling the thread to write the file. Our log thread procedure has a message loop used to receive messages sent by the UI thread. However, after a thread is enabled, the system may not have created a message queue for the thread. Therefore, we require the system to create a message queue for our thread. This is done by calling peekmessage (& MSG, null, wm_user, wm_user, pm_noremove.

Note that the last parameter indicates that the purpose of extracting a message is to "View" without actually "processing" the message, so do not remove the message from the message queue.

The message must be sent after the thread prepares the message queue. Otherwise, the message may fail to be sent. Therefore, after the thread is enabled, create an event to wait for the thread to prepare:

 

Code_startthread
Handle hevent;

Ulong startthread ()
{
Ulong tid =   0 ;
Handle hthread = Createthread (null, 0 , Mythreadfunc, null, 0 , & TID );
If ( ! Hthread) Return   0 ;
Closehandle (hthread );
Return TID;
}

Hevent=Createevent (null, false, false, event_threadqueue );
M_threadid=Startthread ();

//Wait for the thread to establish messagequeue
Waitforsingleobject (hevent,30000);

 

In our thread process, we first called peekmessage and asked the system to create a message queue:

 

Code_threadfunc
# Define Buffer_size 2048
Char M_logbuffer [buffer_size];

DWORD winapi mythreadfunc (lpvoid lpthreadparameter)
{
MSG;
File*Stream=NULL;

//Force the system to create the message queue.
Peekmessage (&MSG, null, wm_user, wm_user, pm_noremove );

If(Hevent! =Null)
Setevent (hevent );

While (Getmessage ( & MSG, null, 0 , 0 ))
{
Switch (Msg. Message)
{
Case Wm_writelog:
Stream = Fopen ( " C: \ logtest \ logtest.txt " , " A " );
If (Stream ! = Null)
{
// -------------- Enter ---------
Entercriticalsection ( & M_cslog );
If (M_logbuffer [ 0 ] ! = 0 )
Fwrite (m_logbuffer, 1 , Strlen (m_logbuffer), stream );
// Clear the buffer (set the first character to 0)
M_logbuffer [ 0 ] = 0 ;
Leavecriticalsection ( & M_cslog );
// -------------- Leave ---------
Fclose (Stream );
}
Break ; // Return cannot be used here, because it is a message loop!
}
}
Return   0 ;
}

 

When the preceding thread receives the wm_writelog message, it accesses the buffer zone, outputs the buffer content to the file, and clears the buffer zone. Note that log buffer is used and accessed by both the UI thread and log thread. When our log thread is in the output buffer content, it is clear that we do not want the UI thread to access it again (for example, input log content to the buffer), otherwise the buffer content will be damaged, this leads to unpredictable results. Therefore, compared with the two threads, logbuffer is a "dedicated" resource. Therefore, we mustCodeAs a critical section, the system ensures that there is only one thread accessing the critical section (having access to resources) at any time. The application must explicitly inform the system that it needs to enter the key code segment (by calling entercriticalsection) and is willing to wait until the resource is idle, when it is accessed, it must be notified that it has left (leavecriticalsection) (otherwise other threads waiting for resources cannot be scheduled ).

We use the following API function to create the critical section:

Critical_section m_cslog;

Initializecriticalsection (& m_cslog); // Initialization
// Here is the use period of criticalsection

Deletecriticalsection (& m_cslog); // release

 

Similarly, in the UI thread, when we want to access logbuffer:

Code
Void Writelogtobuffer ( Char * Text)
{
// -------------- Enter ---------
Entercriticalsection ( & M_cslog );
Strcat (m_logbuffer, text );
Leavecriticalsection ( & M_cslog );
// -------------- Leave ---------
}

VoidWritebuffertofile (ulong threadid)
{
Postthreadmessage (threadid, wm_writelog, null, null );
}

 

The code here is simplified to illustrate the basic problems. The resource division here should be as reasonable as possible, and enter the critical section only when you really need to access the resource, instead of doing any extra work in the criticalsection. At the same time, the division of resources should be as independent as possible. Do not place irrelevant resources in the same section to avoid unnecessary waiting and affect the efficiency of thread scheduling.

 

==========================================

Supplement

==========================================

(1) When the program exits, we hope that the log thread can output data that remains in the buffer zone to the file, but this seems a little difficult. Because we only have one Asynchronous Method of postthreadmessage, and there is no synchronous method that forces the current thread to wait for the processing of the other party to end.

When I first send wm_writelog and then send the wm_quit message to the log thread, I find that the log thread is finished, but it does not seem to process the wm_writelog message before the end. Of course, the specific cause remains to be checked. Because wm_quit is not appended to the queue as we imagine other messages, on the contrary, it only changes a flag that affects the return value of getmessage. The processing process here is transparent to programmers, so I can only guess it by myself. The getmessage function first detects this flag, if "received" wm_quit is found, false is returned. Due to the multi-thread processing method and the particularity of wm_quit, wm_writelog message may fail to be sent because it seems that the thread message queue has been recycled by the system when it tries to enter the column.

Since there is no function such as sendthreadmessage (probably to prevent deadlocks between threads), I adopt a method that seems not reliable but is indeed useful, that is, after sending the wm_writelog message, take the initiative to let out the CPU time slice, let yourself sleep for a moment 200 milliseconds (we are optimistic that the log thread has enough time to process the message during this period), and then send the wm_quit message to end the log thread, it turns out that this method is actually useful, but it seems "blind", so it cannot be a very good and reliable method.

 

[Supplement 2: Supplement] hoodlum1980, 2011-9-2

First, redescribe the issues mentioned in the supplement:

When a process needs to exit, wm_quit is sent to the main thread of the process. Because the log thread periodically writes logs in the cache to the disk, this problem exists. Before the process exits, ensure that the log thread writes the remaining content in the buffer to the disk. However, it is not enough to post a wm_writelog to the log thread at the exit time! (The log thread may not have the opportunity to process the message because it has started to try to end all threads ).

Looking back at this problem, we can find that this is a typical thread synchronization problem.

Therefore, this problem can also be solved using a typical thread synchronization method. Of course, some new changes are required. For example, before postquitmessage, we also use postthreadmessage to notify the log thread (we can define another message, such as wm_writelog2) to save the buffer content to the disk file, at this time, we create a signal or event, and then wait for it with waitsingleobject. If the log thread is fully saved, we will set the signal/event.

This is much more reliable than the previous method of "blindly waiting for 200 milliseconds.

 

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.