Home > front end >  Execute lambda with CreateThread
Execute lambda with CreateThread

Time:09-09

Is there a better way to use CreateThread than creating a free function each time for the sole purpose of casting lpParameter?

Are there any modern alternatives to CreateThread for creating persistent threads?

Edit: Perhaps you should just use std::async(lambda). I imagine that it's just implemented with CreateThread. Maybe the answer to this question is looking up how std::async is implemented (assuming it's a library feature).

DWORD WINAPI MyThreadFunction(
  _In_ LPVOID lpParameter
)
{
    ((MyClass*)lpParameter)->RunLoop();
}

void MyClass::LaunchThread()
{
    CreateThread( 
            NULL,                   // default security attributes
            0,                      // use default stack size  
            MyThreadFunction,       // thread function name
            this,          // argument to thread function 
            0,                      // use default creation flags 
            NULL);   // returns the thread identifier 
}

CodePudding user response:

std::async() and std::thread(, <args...>) are most likely internally implemented as you just did, the only exception is that lambdas without captures can be implicitly converted to function pointers, which pretty much can be passed straight away to CreateThread function with nullptr lpParameter. Lambdas with capture list are pretty much syntactic sugar but internally they translate to sth like this (very simplified):

struct <internal_lambda_name>
{
    <capture list...> fields...;

    void operator()(<arguments...>){<code...>;}
};

So they pretty much translate to objects of struct type thus they need some way to store all those captures and in order to be executed on other thread with CreateThread function they need some way of ensuring that the capture list data stored in them will be available during their execution.

CodePudding user response:

I looked in to MSVC implementation of std::async and they implemented it using ::Concurrency::create_task which straight forwardly accepts a callable object.

https://docs.microsoft.com/en-us/cpp/parallel/concrt/task-parallelism-concurrency-runtime

I also looked into their implementation of create_task

template<typename _Ty>
__declspec(noinline) // Ask for no inlining so that the _CAPTURE_CALLSTACK gives us the expected result
explicit task(_Ty _Param)
{
    task_options _TaskOptions;
    details::_ValidateTaskConstructorArgs<_ReturnType,_Ty>(_Param);

    _CreateImpl(_TaskOptions.get_cancellation_token()._GetImplValue(), _TaskOptions.get_scheduler());
    // Do not move the next line out of this function. It is important that _CAPTURE_CALLSTACK() evaluates to the call site of the task constructor.
    _SetTaskCreationCallstack(_CAPTURE_CALLSTACK());

    _TaskInitMaybeFunctor(_Param, decltype(details::_IsCallable(_Param,0))());
}

and so it turns out that launching a lambda on a new thread is quite difficult and beyond the scope of this question.

CodePudding user response:

There are several mechanisms for achieving parallelism (std::async etc. as mentioned above).

But the modern one which is most similar to your original code with CreateThread is std::thread.
It can be constructed with a global function, a lambda, or a class method (which seems the best fit for you). In the case of a class method you also need to specify the class instance when constructing the thread.
Note that a std::thread starts to run (potentially) when constructed.

Here's a complete example:

#include <thread>
#include <chrono>
#include <atomic>
#include <iostream>

class MyClass
{
public:
    MyClass()  {}
    ~MyClass() { EndThread(); }

    void LaunchThread()
    {
        EndThread();    // in case it was already running
        m_bThreadShouldExit = false;
        // Start the thread with a class method:
        m_thread = std::thread(&MyClass::RunLoop, this);
    }

    void EndThread()
    {
        // Singal the thread to exit, and wait for it:
        m_bThreadShouldExit = true;
        if (m_thread.joinable())
        {
            m_thread.join();
        }
    }

    void RunLoop()
    {
        std::cout << "RunLoop started" << std::endl;
        while (!m_bThreadShouldExit)
        {
            std::cout << "RunLoop doing something ..." << std::endl;
            std::this_thread::sleep_for(std::chrono::milliseconds(1000));
        }
        std::cout << "RunLoop ended" << std::endl;
    }

private:
    std::thread         m_thread;
    std::atomic_bool    m_bThreadShouldExit{ false };
};


int main()
{
    MyClass m;
    m.LaunchThread();
    std::this_thread::sleep_for(std::chrono::milliseconds(5000));
    m.EndThread();
}

Possible output:

RunLoop started
RunLoop doing something ...
RunLoop doing something ...
RunLoop doing something ...
RunLoop doing something ...
RunLoop doing something ...
RunLoop ended

Note:
If you would like to use a lambda for the thread method you can do it as well:
Instead of this line:

m_thread = std::thread(&MyClass::RunLoop, this);

Use:

m_thread = std::thread([this](){ RunLoop(); });  // Start with a lambda
  • Related