Tracing thread creation points for debugging
I've come to love lambdas, and a while ago I wrote a simple wrapper which takes a lambda and starts it on a new thread.
//
// Starts a task on a separate thread, when passed a lambda expression
//
template<typename T>
smart_ptrs::w32handle StartTask(T f)
{
// Make a copy of the task on the heap
T* pTask = new T(f);
// Create a new thread to service the task
smart_ptrs::w32handle hThread(::CreateThread(
NULL,
0,
(LPTHREAD_START_ROUTINE)& detail::start_task_proc<T>,
(LPVOID) pTask,
NULL,
NULL));
// If the caller ignores this rc, the thread handle will simply close and the
// thread will continue in fire-and-forget fashion.
return hThread;
}
NOTE : Yes I know this doesn't really need a template and could happily use std::function
. I've posted it this way because it matches more complicated (async queue) versions I have which do have to be templates.
The end result is a function which is very easy to use in parallel algorithms etc. However a problem arises if you start to use such a function extensively. Because the threads created all appear to come from the same rather generic function it can become difficult to tell where in the code they were started. It's usually possible to work out from the context of what they're doing but it's not as easy as it used to be when using an explicit thread function. Does anyone have a good method of tagging such threads so that they're easier to debug?
As far as I can tell from the code, you'll have threads that have a start_task_proc
somewhere in their callstack that call their function object. You could modify that function to take a pointer to a "task info" structure, rather than the bare function object. You could stuff whatever information you like into that information object, eg line numbers and filenames of where you created the task:
template <class T>
struct TaksInfo {
T func;
unsigned line;
char const* file;
TaskInfo(T&& t, unsigned l, char const* f)
: func(std::move(t), line(l), file(f) {}
};
template<typename T>
smart_ptrs::w32handle StartTask(T f, unsigned line, char const* file)
{
// Make a copy of the task on the heap
auto pTask = new TaskInfo<T>(f, line, file);
// Create a new thread to service the task
smart_ptrs::w32handle hThread(::CreateThread(
NULL,
0,
(LPTHREAD_START_ROUTINE)& detail::start_task_proc<T>,
(LPVOID) pTask,
NULL,
NULL));
// If the caller ignores this rc, the thread handle will simply close and the
// thread will continue in fire-and-forget fashion.
return hThread;
}
#define START_TASK(f) StartTask(f, __LINE__, __FILE__)
template <class T>
DWORD start_task_proc(LPVOID lpParameter) {
auto pTask = (TaskInfo<T>*) lpParameter;
return pTask->func();
}
//use:
int main() {
auto threadHandle = START_TASK(([]() -> DWORD { std::cout << "foon"; return 42;} ));
}
If you now inspect pTask
in start_task_proc
you can see file
and line
wich can tell you where the task was started.
Of course you could avoid the TaskInfo
structure and make the information just a template parameter of start_task_proc
:
template <class T, unsigned Line, char const* File>
DWORD start_task_proc(LPVOID lpParameter) { /* as you have it */)
template<unsigned Line, char const* File, typename T> //use T last fur type deduction
smart_ptrs::w32handle StartTask(T f)
{
// Make a copy of the task on the heap
auto pTask = new T(std::move(f));
// Create a new thread to service the task
smart_ptrs::w32handle hThread(::CreateThread(
NULL,
0,
(LPTHREAD_START_ROUTINE)& detail::start_task_proc<T, Line, File>, //!!!
(LPVOID) pTask,
NULL,
NULL));
// If the caller ignores this rc, the thread handle will simply close and the
// thread will continue in fire-and-forget fashion.
return hThread;
}
#define START_TASK(f) StartTask<__LINE__, __FILE__>(f)
链接地址: http://www.djcxy.com/p/13838.html
上一篇: 具有混合虚拟和非混合的C ++ 11类格
下一篇: 跟踪线程创建点以进行调试