Member function thread Adapter

Source: Internet
Author: User

Sometimes, due to the design needs, we usually need to start a class member function in the thread mode, and the most common implementation methods are two: 1. directly declare the member function as static, so that functions such as CreateThread can be directly called; 2. declare a static member function as a proxy and call non-static member functions of the class internally.

These two methods appear only for the purpose of implementation, with almost no advantages, but the disadvantages are obvious: For method 1, there will certainly be forced type conversion in the static member function to get pThis, then a pile of pThis-> XXX; For method 2, static member functions acting as proxies are not reusable. If a new thread is designed, another Proxy function should be written to repeat the work.

Another acceptable solution is to compile a thread-based class and provide a virtual function similar to run. The derived class implements the run function. However, this solution also has shortcomings: first, the user's class must be derived from this thread class and coupling is introduced. Second, if the user's class has multiple member functions that need to be started by threads, then, a single run virtual function of the thread base class cannot meet the requirements. That is to say, the thread base class restricts the capabilities of the user class.

The limitations of the thread-based class expose the disadvantages of the intrusion-type program framework. The so-called intrusion-type program framework means that functional code must be written according to certain rules in order to obtain the support of the framework. The intrusive framework restricts the thinking, functional limitations, and poor scalability of designers. A typical intrusive framework example is the template method of the design pattern.

Correspondingly, there is also a non-invasive program framework: the encoding rules are random, and the framework is not constrained. As long as the user's class is managed by the framework, you can get some support. A typical non-invasive framework is the spring ioc container. As long as the user class is managed by the ioc container, such capabilities as singleton, prototype, and aop can be obtained.

In the field of program design, if functions can be implemented in non-intrusive mode, they should not be implemented in intrusive mode. Back to the thread-based problem of class member functions: Is there a simple way to achieve consistency without changing the definition of the class (that is, the so-called non-intrusion method ), so that threads can adapt to non-static member functions of any class? Yes. This is the member function thread adapter introduced in this article.

In fact, the implementation of the member function thread adapter is very simple, that is, the static member function of method 2 is parameterized using the template, so that its universality can be improved. Step 3: first, store non-static member functions and object instances. Second, you need to implement a general threadproc as a thread function. The function internally calls the saved member function, the first two steps are implemented through templates. Finally, some security mechanisms are required to ensure the thread can be started safely. To view the Code:

Template <typename Result, typename T>

Class mem_fun_thread_t

{

Public:

Mem_fun_thread_t (Result (T: * _ Pm) (), T & inst): mFunDetail (_ Pm), tInstance (inst)

{

HEvent = CreateEvent (NULL, 0, 0, NULL );

}

 

HANDLE BeginThread ()

{

HANDLE hThread = CreateThread (0, 0, this-> thread_proc, this, 0, 0 );

If (hThread = NULL)

{

Return NULL;

}

WaitForConstruct ();

Return hThread;

}

 

~ Mem_fun_thread_t ()

{

CloseHandle (hEvent );

}

Private:

HANDLE hEvent;

T & tInstance;

Mem_fun_ref_t <Result, T> mFunDetail;

 

Static DWORD _ stdcall thread_proc (void * p)

{

Mem_fun_thread_t <Result, T> * tmp = (mem_fun_thread_t <Result, T> *) p;

T & inst = tmp-> tInstance;

Mem_fun_ref_t <Result, T> funDetail = tmp-> mFunDetail;

SetEvent (tmp-> hEvent );

FunDetail (inst );

Return 0;

}

 

Void WaitForConstruct ()

{

WaitForSingleObject (hEvent, INFINITE );

 

}

};

Mem_fun_thread_t is the main character. Its constructor stores the non-static member function of the template parameter T and an instance of T, and also creates an automatic reset event. The role of this event will be discussed later.

BeginThread is the interface that the client program uses to start a thread. It uses CreateThread internally to create a thread. The thread parameters are thread_proc and this. When CreateThread is complete, the function will call WaitForConstruct to wait. What are you waiting for? I will answer the question when talking about the automatic reset event. After the process is completed, the function exits.

Let's look at thread_proc. First, copy the function parameter (the CreateThread parameter this, that is, mem_fun_thread_t) to the local variable of the function, and set the event bit. After the event is set, BeginThread can return. After the event is set, call the member function using the object instance in the local variable.

Here, I will explain why we need to copy this data to the local variable of the thread function. this is because this stores the member functions and object instances, we need to ensure that the data is valid during thread execution. If it is not copied to the local variable of the thread function, the data may be invalid after the client program calls BeginThread, for example, the adapter is a temporary variable: mem_fun_thread_t <bool, Test> (& Test: func, test ). beginThread (); secondly, we must ensure that the data is valid during the replication process and can be destroyed only after the replication is complete. Therefore, WaitForConstruct appears in BeginThread. WaitForConstruct does not return until SetEvent is called in thread_proc. When SetEvent is called, the data has been successfully copied to thread_proc.

Therefore, under normal circumstances, when BeginThread returns, it can ensure that 1. The thread has started; 2. the data required by the thread has been safely saved.

Okay. Let's see how the customer program is used:

Class Test

{

Public:

Bool func ()

{

Return true;

}

 

 

Void ActivatePrivateFunc ()

{

CloseHandle (mem_fun_thread_t <bool, Test> (& Test: privateFun, * this). BeginThread ());

}

Private:

Bool privateFun ()

{

Return false;

}

};

 

Int main (int argc, char * argv [])

{

Test test;

 

CloseHandle (mem_fun_thread_t <bool, Test> (& Test: func, test). BeginThread ());

 

Return 0;

}

The code above demonstrates how to make a public and private parameter-free member function a thread. Of course, you can also use mem_fun_thread_t to create an object and save it. You can call BeginThread only when necessary. In addition, BeginThread can also be called multiple times to create multiple threads.

Disadvantages: This adapter can only adapt to non-const member functions. If it is a const member function, you need to write a const adapter, just like the relationship between mem_fun_t and const_men_fun_t. In addition, you may have discovered that this adapter can only adapt to non-argument member functions. Yes, if you want to implement the existing parameter version, you can modify the code and replace mem_fun_ref_t with mem_fun1_ref_t, then add the template parameters and member variables (mem_fun1_thread_t ). Yes. After modification, only single-parameter member functions can be adapted. If there are two parameters, you need to use the boost library. What if there are more parameters?

Generally, the data required by a member function used as a thread can be obtained from its own object. Therefore, I think the member function without parameters already meets the requirements.

Finally, you may not be familiar with mem_fun_ref_t. Here is a brief introduction. In the standard library's functional header file, mem_fun_ref_t is used to adapt a class member function to a single-Parameter function object (function object or functor), for example, object. func () is transformed into functor (object) after it is adapted ). Compared with function pointers, it is difficult to understand them, and function objects are advantageous. It has all the advantages of OOP. In addition, there is actually a very general program design concept (but it originates from the ancient theory lambda calculus): functional programming, which is called functional programming (FP. Although the standard library functional header file only provides some basic FP facilities, some classic FP methods can still be reproduced, for example, bind1st adapts a double parameter function to a single parameter function object (FP calls this conversion partial function ). If you are interested in FP, refer to http://en.wikipedia.org/wiki/functional_programming.

In other words, mem_fun (x) _ thread_t, x> = 0 can actually be more magnificent, that is, the BeginThread function is replaced by operator (). If you do this, the following code will appear:

Typedef mem_fun1_thread_t <bool, Test, int> TestThreadFunc;

TestThreadFunc testFunc = TestThreadFunc (& Test: func1, test );

HANDLE hThread = testFunc (10 );

It seems that testFunc is like a common single-Parameter Function call, but it has actually started the thread pai_^ and the thread uses the testFunc parameter to call a member function of the class. Objects like testFunc are called closure in FP. Attached mem_fun1_thread_t code:

Template <typename Result, typename T, typename Param>

Class mem_fun1_thread_t

{

Public:

Mem_fun1_thread_t (Result (T: * _ Pm) (Param), T & inst): Pm (_ Pm), tInstance (inst)

{

HEvent = CreateEvent (NULL, 0, 0, NULL );

}

 

HANDLE operator () (const Param & param)

{

This-> tParam = param;

HANDLE hThread = CreateThread (0, 0, this-> thread_proc, this, 0, 0 );

If (hThread = NULL)

{

Return NULL;

}

WaitForConstruct ();

Return hThread;

}

~ Mem_fun1_thread_t ()

{

CloseHandle (hEvent );

}

Private:

HANDLE hEvent;

T & tInstance;

Param tParam;

Mem_fun1_ref_t <Result, T, Param> Pm;

 

Static DWORD _ stdcall thread_proc (void * p)

{

Mem_fun1_thread_t <Result, T, Param> * tmp = (mem_fun1_thread_t <Result, T, Param> *) p;

 

T & inst = tmp-> tInstance;

Param param = tmp-> tParam;

Mem_fun1_ref_t <Result, T, Param> funcDetail = tmp-> Pm;

 

SetEvent (tmp-> hEvent );

FuncDetail (inst, param );

Return 0;

}

 

Void WaitForConstruct ()

{

WaitForSingleObject (hEvent, INFINITE );

}

};

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.