0

Is there a way, using C++11 threads, to spawn a thread which can somehow access class members?

Let's say I instantiate an object like this,

    FITS_file <float> fits_file;

where the class is defined in a header file as:

template <class T>
class FITS_file {
  private:
    std::mutex fits_mutex;                 //!< used to block writing_file semaphore in multiple threads
    bool writing_file;                     //!< semaphore indicates file is being written
    std::unique_ptr<CCfits::FITS> pFits;   //!< pointer to FITS data container

  public:
    FITS_file() {
      this->writing_file = false;
    };

    long write_image(T* data, Archon::Information& info) {
      std::thread(write_image_thread, array, info).detach();                      // spawn thread here
      return 0;
    }

    static void write_image_thread(std::valarray<T> &data, Archon::Information &info) {
      // lock mutex, set writing_file=true, does the work, set writing_file=false
      // must be static (?) for C++ threads
      // but can't access this->fits_mutex and friends because it's static
    }

The worker thread (write_image_thread) has to be static but if it's static then I can't access this-> members inside the thread.

I tried spawning the thread like this:

std::thread([&](){this->write_image_thread(array, info);}).detach();

but (a) I don't know if that's right (even though it compiles); and (b) I seem to be limited in what I can pass to the thread; and (c) I still can't access this-> members.

I understand I can do what I want if I use Boost threads, and maybe I just have to do that, but I was wondering if there was a way there from here using straight C++11.

astronomerdave
  • 512
  • 1
  • 5
  • 18
  • 3
    Did you try making `write_image_thread` a regular class method, instead of a static thread function, and them simply calling `std::thread(&FITS_file, this);`, with `write_image_thread` accessing its members normally, like any other class method? Note that it's your responsibility to make sure (in some way) that the object exists until the thread terminates. – Sam Varshavchik Apr 04 '20 at 00:27
  • To add to Sam: _to make sure (in some way) that the object exists until the thread terminates_ For this, I would drop the `detach()` and make the `std::thread` a member of the class. Then the destructor can check the thread for `joinable()` and `join()` the thread in case. – Scheff's Cat Apr 04 '20 at 05:37
  • @SamVarshavchik I think that's the answer ... see the full answer I proposed. – astronomerdave Apr 06 '20 at 20:48

1 Answers1

0

Based on the comments I think I've come up with an answer. I used @Sam Varshavchik's suggestion and I define the thread function as:

void write_image_thread(std::valarray<T> &data, Archon::Information &info, FITS_file<T> *obj) {
    const std::lock_guard<std::mutex> lock(obj->fits_mutex);  // access private members!
    obj->writing_file = true;                                 // access private members!
      try {
        obj->pFits->pHDU().addKey("EXPOSURE", fpixel,"Total Exposure Time");
        // and more stuff...
      }
      catch (CCfits::FitsError& error){
        // handle faults
      }

      // all done
      obj->writing_file = false;
    }

then call it with:

std::thread([&](){this->write_image_thread(std::ref(array), std::ref(info), this);}).detach();

and this seems to work! So I think this is the answer (I'm going to try to exercise it a bit more before tagging this as answered, to be safe).

Not sure if it's good practice to ask addition questions but they're related...

1) are the std::ref() functions really necessary? I added them to be explicit but it seems to work without them.

2) I have to admit I don't REALLY understand the lambda expression I'm using; I found that on another Stack Overflow question (C++ 11 Thread initialization with member functions compiling error -- in a comment on the original question). I've tried various ways to make it work without the lambda but can't get it to compile otherwise.

In other words, this:

std::thread(&FITS_file<T>::write_image_thread, std::ref(array), std::ref(info), this).detach();

does not work; it gives the following error:

In file included from /usr/include/c++/4.8.2/mutex:42:0,
                 from /home/user/archon-interface/include/common.h:13,
                 from /home/user/archon-interface/src/archon.cpp:9:
/usr/include/c++/4.8.2/functional: In instantiation of ‘struct std::_Bind_simple<std::_Mem_fn<void (FITS_file<float>::*)(std::valarray<float>&, Archon::Information&, FITS_file<float>*)>(std::reference_wrapper<std::valarray<float> >, std::reference_wrapper<Archon::Information>, FITS_file<float>*)>’:
/usr/include/c++/4.8.2/thread:137:47:   required from ‘std::thread::thread(_Callable&&, _Args&& ...) [with _Callable = void (FITS_file<float>::*)(std::valarray<float>&, Archon::Information&, FITS_file<float>*); _Args = {std::reference_wrapper<std::valarray<float> >, std::reference_wrapper<Archon::Information>, FITS_file<float>* const}]’
/home/user/archon-interface/include/fits.h:137:94:   required from ‘long int FITS_file<T>::write_image(T*, Archon::Information&) [with T = float]’
/home/user/archon-interface/src/archon.cpp:1697:52:   required from here
/usr/include/c++/4.8.2/functional:1697:61: error: no type named ‘type’ in ‘class std::result_of<std::_Mem_fn<void (FITS_file<float>::*)(std::valarray<float>&, Archon::Information&, FITS_file<float>*)>(std::reference_wrapper<std::valarray<float> >, std::reference_wrapper<Archon::Information>, FITS_file<float>*)>’
       typedef typename result_of<_Callable(_Args...)>::type result_type;
                                                             ^
/usr/include/c++/4.8.2/functional:1727:9: error: no type named ‘type’ in ‘class std::result_of<std::_Mem_fn<void (FITS_file<float>::*)(std::valarray<float>&, Archon::Information&, FITS_file<float>*)>(std::reference_wrapper<std::valarray<float> >, std::reference_wrapper<Archon::Information>, FITS_file<float>*)>’
         _M_invoke(_Index_tuple<_Indices...>)
         ^
make[2]: *** [CMakeFiles/archonserver.dir/src/archon.cpp.o] Error 1
make[1]: *** [CMakeFiles/archonserver.dir/all] Error 2
make: *** [all] Error 2

But as I said, if I use the lambda then it works! So maybe I should just be happy with that.

astronomerdave
  • 512
  • 1
  • 5
  • 18