2013-06-03 9 views
7

Ich bin gekommen, Lambda-Ausdrücke zu lieben, und vor einer Weile habe ich eine einfache Hülle, die eine Lambda nimmt und beginnt es auf einem neuen Thread.Tracing Thread-Erzeugung Punkte für das Debuggen

// 
// 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; 
} 

HINWEIS: Ja, ich weiß, das muss nicht wirklich eine Vorlage und glücklich std::function nutzen könnten. Ich habe es auf diese Weise gepostet, weil es mit komplizierteren (asynchronen) Versionen übereinstimmt, die ich habe, die Vorlagen sein müssen.

Das Endergebnis ist eine Funktion, die sehr einfach ist, usw. in parallelen Algorithmen verwenden jedoch ein Problem entsteht, wenn Sie umfassend eine solche Funktion zu nutzen beginnen. Da die erzeugten Threads scheinbar alle von der gleichen generischen Funktion stammen, kann es schwierig werden zu sagen, wo im Code sie gestartet wurden. Es ist normalerweise möglich, aus dem Kontext dessen, was sie tun, heraus zu arbeiten, aber es ist nicht so einfach wie früher, wenn eine explizite Thread-Funktion verwendet wird. Hat jemand eine gute Methode, solche Threads zu markieren, damit sie einfacher zu debuggen sind?

+3

Wenn Sie lambdas verwenden, scheinen Sie C++ 11-Unterstützung zu haben. Aber wenn du das hättest, warum würdest du dieses ungeschickte WinAPI-Thread-Zeug anstelle von C++ 11s "std :: thread" verwenden? –

+0

Beantworten Sie die Frage nicht selbst, aber kennen Sie ['std :: async'] (http://en.cppreference.com/w/cpp/thread/async)? – Angew

+0

OT: Die Verwendung der Template-Argumentableitung über die std :: -Funktion ist insgesamt flexibler und potenziell effizienter (hier nicht anwendbar, wette ich). – sehe

Antwort

3

Soweit ich aus dem Code erkennen kann, haben Sie Threads, die eine start_task_proc irgendwo in ihrem Callstack haben, die ihr Funktionsobjekt aufrufen. Sie könnten diese Funktion ändern, um einen Zeiger auf eine "Task-Info" -Struktur anstelle des bare-Funktionsobjekts zu setzen. Sie könnten die Information, die Sie mögen, in dieses Informationsobjekt stopfen, z. Zeilennummern und Dateinamen in dem Sie die Aufgabe erstellt:

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 << "foo\n"; return 42;})); 
} 

Wenn Sie jetzt pTask in start_task_proc inspizieren können Sie file und line wich sehen kann Ihnen sagen, wo die Task gestartet wurde.
Natürlich können Sie die TaskInfo Struktur vermeiden konnte und die Informationen nur ein Template-Parameter von start_task_proc machen:

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) 
+0

+1, sehr elegante Lösung. –

+0

Ich mag das, relativ wenig Overhead zu. – Benj