简体   繁体   中英

How to use a thread inside a class constructor in C++?

Is there any possible way to use threads inside a class like this:

Class Logger // singleton class
{
//.... here is implemented thread logic 
{

void main()
{
   Logger& log = Logger::getInstance();
   log.writeMessage(); // this should execute in another thread
                       // also, all the other func from Logger class
                       // should execute in that second thread

   // ... some code -> executes in main thread

   log.deleteInstance(); // join threads

}

I need to mention that I'm new to threading. I just need an idea so I can start thinking how it works.

What you're describing is a worker thread that will run in the background. I wouldn't start a new thread every time you call WriteMessage() as thread creation is fairly expensive, and starting and stopping your thread can actually slow down your program. Instead you can start a thread in the constructor of the class and let it monitor a queue. Other clients of your Logger class can use the WriteMessage() function to push something onto the queue. The logger will detect some job has arrived and process it. At the end when you're finished call a Stop() function to stop the thread.

To do all this your thread has to execute a function that runs a loop. You can use a condition variable to wait on a condition, ie a job request or stop command. The advantage of a condition variable is that all the thread synchronization is done for you. You just have to specify the condition as a predicate. Putting something on the queue would have to be an atomic operation. You can use a std::lock_guard for that.

You can call other functions in Logger from the main thread while the worker thread sits in the background doing its job. That's not a problem.

Here's an implementation of this Logger class:

#include <thread>
#include <mutex>
#include <condition_variable>
#include <chrono>
#include <iostream>
#include <queue>

class Logger // singleton class
{
public:
    Logger() : mThread{}, mCV{}, mMutex{}, mQueue{}, mStop{ false }
    {
        mThread = std::thread(&Logger::Run, this); //create thread and execute Run()
    }

    ~Logger()
    {
        //Join thread
        if (mThread.joinable())
        {
            mThread.join();
        }
    }

    static Logger& getInstance() {
        static Logger logger;
        return logger;
    }

    void Stop()
    {
        {
            //Set the stop flag
            std::lock_guard<std::mutex> lk(mMutex);
            mStop = true;
        }
        mCV.notify_one();
    }

    void WriteMessage(const std::string& msg)
    {
        {
            //Push a request on the queue
            std::lock_guard<std::mutex> lk(mMutex);
            mQueue.push(msg);
        }
        mCV.notify_one();
    }

private:
    void Run()
    {
        while (true)
        {
            //Wait until some request comes or stop flag is set
            std::unique_lock<std::mutex> lock(mMutex);
            mCV.wait(lock, [&]() { return mStop || !mQueue.empty(); });

            //Stop if needed
            if (mStop)
            {
                break;
            }

            //Pop the job off the front of the queue
            std::string msg = std::move(mQueue.front());
            mQueue.pop();
            //Unlock the mutex
            lock.unlock();

            std::cout << msg << std::endl;
        }
    }

private:
    std::thread mThread;
    std::condition_variable mCV;
    std::mutex mMutex;
    std::queue<std::string> mQueue;
    bool mStop;
};

Working version here: https://ideone.com/wYIaMY

You can execute code in a second thread with a lambda expression like so:

void main()
{
    Logger& log = Logger::getInstance();
    std::thread thread([] () {
        Logger& log = Logger::getInstance();
        log.writeMessage(); // second thread
    });
    log.writeMessage(); // main thread

    thread.join(); // wait for second thread to finish
    log.deleteInstance();
}

What you can't (easily) do is call writeMessage() in the main thread and expect it to do its work in the second. Calls don't "jump across threads"; you call a function, it executes in the ongoing thread, it returns, and you continue on in the ongoing thread.

Yes, there is nothing preventing you from using threads in the member functions of a class. However, there are many ways to approach this problem.

You could create a std::thread in the constructor of class Logger , and then have member functions which are called in the main thread somehow signal the secondary thread to do some work on its behalf. That will typically involve a message queue. It's not too hard to implement, but also not trivial.

A simpler approach would be to just use std::async() to run a function asynchronously. You can either have writeMessage() itself call std::async() , but then the question is how it will clean up after itself. The simplest is to just call std::async() from the main thread:

void main() {
    Logger &log = Logger::getInstance();
    {
        auto future = std::async(std::launch::async, [&log]{log.writeMessage});
        // ... some code -> executes in main thread
    } // exiting this scope waits for the async call to finish
    log.deleteInstance(); 
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM