Chris XJ Zhu
Chris XJ Zhu

Reputation: 63

How to invoke member function safely when inheriting std::thread

My code is as below:

  class MyThread : public std::thread {
        int a_;

    public:
        MyThread(int a)
            : std::thread(&MyThread::run, this),
              a_(a)
        { }

        void run() {
            // use a_
        }
    };

I want to have my own thread class which has all the methods that std::thread provides, so I let MyThread class inherit std::thread. In MyThread's constructor, I pass its member function to std::thread. The compilation is OK, but I am concerned it there a race condition between invoking run() in std::thread's construtor and initializing a_.

Is there a way to make it safe?

Upvotes: 0

Views: 213

Answers (3)

Jarod42
Jarod42

Reputation: 218268

but I am concerned it there a race condition between invoking run() in std::thread's construtor and initializing a_.

Yes,

You might reorder member/base:

struct MyThreadData
{
    int _a;
};

struct MyThread : MyThreadData, std::thread
{
public:
   explicit MyThread(int a) : MyThreadData{a}, _thread([this] {run();}) {}

    void run()
    {
       // thread code here
    };
};

Upvotes: 0

selbie
selbie

Reputation: 104589

Don't do it that way. "Has-a" (composition) has a lot of advantages over "is-a" (inheritance).

class MyThread
{
    std::thread _thread;
    int _a;
public:

    MyThread(int a) : _a(a)
    {
        _thread = std::thread([this] {run();});
    }

    void run()
    {
       // thread code here
    };

    void join()
    {
        _thread.join();
    }
};

A better approach would be to recognize that the thread and the operation on that thread are two distinct objects:

class WorkerOperation
{
    int _a;
public:
   WorkerOperation(int a) :  _a(a)
   {
   }

   void run()
   {
     // your code goes here
   }
};

And then to create a thread:

shared_ptr<WorkerOperation> spOp = make_shared<WorkerOperation>(42);
std::thread t = std::thread([spOp] {spOp->run();});

And if you really need to pair up the operation and the thread:

std::pair<WorkerOperation, std::thread> threadpair;
threadpair.first = spOp;
threadpair.second = std::move(t);

Upvotes: 3

ALX23z
ALX23z

Reputation: 4713

To fix the error you can write:

    MyThread(int a)
        :a_(a)
    { 
        (std::thread&)(*this) = std::thread(&MyThread::run, this);
     }

This way, the thread is run with initialized a_.

In general, I don't think it is a good idea to inherit from std::thread. Better make it a private member and run it. Otherwise user can do weird shit if you allow them to cast your class to std::thread& publicly. Like executing a different function than what you intended.

Upvotes: 1

Related Questions