简体   繁体   中英

Invoking a function automatically on std::thread exit in C++11

I want to set up an invocation of a function (or lambda function) to happen automatically when the current thread exits, but I cannot see any way to do it that works with std::thread unless I take over the entire task of thread creation or manually ensure that every thread calls some particular function that I always provide as its very last operation.

Essentially, I want function whose prototype may resemble something like this:

on_thread_exit(const std::function<void()> &func);

Which would perform whatever setup was necessary to ensure that the given function was automatically called when the thread which called on_thread_exit eventually terminates, and without requiring any particular functions to be explicitly called at thread creation or termination.

You can do it using thread_local storage, since C++11 should call the destructors after the thread exits. You'll have to make sure you have a compiler that supports that properly.

#include <stack> 
#include <function>

void on_thread_exit(std::function<void()> func)
{
  class ThreadExiter
  {
    std::stack<std::function<void()>> exit_funcs;
  public:
    ThreadExiter() = default;
    ThreadExiter(ThreadExiter const&) = delete;
    void operator=(ThreadExiter const&) = delete;
    ~ThreadExiter()
    {
      while(!exit_funcs.empty())
      {
        exit_funcs.top()();
        exit_funcs.pop();
      }
    }
    void add(std::function<void()> func)
    {
      exit_funcs.push(std::move(func));
    }   
  };

  thread_local ThreadExiter exiter;
  exiter.add(std::move(func));
}

Basically, this function creates a thread_local object of the above class. This is basically static, except is destroyed when the thread exits. When called, it pushes the function onto the vector, and when its destroyed it runs the functions.

You can use it by calling on_thread_exit() from any thread, which will create the exit object, which will run your function in the reverse order that it was put into the thread queue (feel free to modify as you see fit).

Here is a simplified/shortened version based on Dave S's solution that uses more C++11 features with the following properties:

  • Since ThreadExiter is only used once, we can combine the variable declaration, avoid public / private ( class to struct ) and remove the copy constructor/assignment operator
  • Replace std::stack with range-based for loop and std::deque
  • Add to the member variable directly instead of having an add() method

Note that this destructs the callback functions from the std::deque only after all have been called, which might or might not be what you want -- if you require the function object to be destroyed after it has been called (and before any other function objects are called), use a stack and pop elements like in Dave's solution).

Code:

#include <functional>
#include <deque>

void
on_thread_exit(std::function<void()> func)
{
    thread_local struct ThreadExiter {
        std::deque<std::function<void()>> callbacks;

        ~ThreadExiter() {
            for (auto &callback: callbacks) {
                callback();
            }
        }
    } exiter;

    exiter.callbacks.emplace_front(std::move(func));
}

You may use BOOST_SCOPE_EXIT

For the the main thread:

void on_thread_exit();

void main( int argc, char** argv )
{
   BOOST_SCOPE_EXIT() // may be one arg, at least, is required 
   {
      on_thread_exit();
   }BOOST_SCOPE_EXIT_END

   ...
}

I leave you to extrapolate any thread !

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