简体   繁体   English

如何在 C++ 的 class 构造函数中使用线程?

[英]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 中使用线程,如下所示:

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.我不会在每次调用WriteMessage()时启动一个新线程,因为创建线程的成本相当高,而且启动和停止线程实际上会减慢程序的速度。 Instead you can start a thread in the constructor of the class and let it monitor a queue.相反,您可以在 class 的构造函数中启动一个线程并让它监视一个队列。 Other clients of your Logger class can use the WriteMessage() function to push something onto the queue. Logger class 的其他客户端可以使用WriteMessage() function 将某些内容推送到队列中。 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.最后,当你完成调用Stop() function 来停止线程。

To do all this your thread has to execute a function that runs a loop.为此,您的线程必须执行一个运行循环的 function。 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.您可以为此使用std::lock_guard

You can call other functions in Logger from the main thread while the worker thread sits in the background doing its job.您可以从主线程调用Logger中的其他函数,而工作线程则位于后台执行其工作。 That's not a problem.这不是问题。

Here's an implementation of this Logger class:这是此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这里的工作版本: https://ideone.com/wYIaMY

You can execute code in a second thread with a lambda expression like so:您可以使用 lambda 表达式在第二个线程中执行代码,如下所示:

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.你不能(容易)做的是在主线程中调用writeMessage()并期望它在第二个中完成它的工作。 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.您调用 function,它在正在进行的线程中执行,它返回,然后您在正在进行的线程中继续。

Yes, there is nothing preventing you from using threads in the member functions of a class.是的,没有什么可以阻止您在 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.您可以在class Logger的构造函数中创建一个std::thread ,然后让在主线程中调用的成员函数以某种方式指示辅助线程代表它做一些工作。 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.一种更简单的方法是仅使用std::async()异步运行 function 。 You can either have writeMessage() itself call std::async() , but then the question is how it will clean up after itself.您可以让writeMessage()本身调用std::async() ,但问题是它如何自行清理。 The simplest is to just call std::async() from the main thread:最简单的是从主线程调用std::async()

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(); 
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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