简体   繁体   中英

How to say to std::thread to stop?

I have two questions.

1) I want to launch some function with an infinite loop to work like a server and checking for messages in a separate thread. However I want to close it from the parent thread when I want. I'm confusing how to std::future or std::condition_variable in this case. Or is it better to create some global variable and change it to true/false from the parent thread.
2) I'd like to have something like this. Why this one example crashes during the run time?

#include <iostream>
#include <chrono>
#include <thread>

#include <future>

std::mutex mu;
bool stopServer = false;

bool serverFunction()
{

    while (true)
    {
        // checking for messages...
        // processing messages

        std::this_thread::sleep_for(std::chrono::seconds(1));

        mu.lock();

        if (stopServer)
            break;

        mu.unlock();
    }

    std::cout << "Exiting func..." << std::endl;

    return true;
}

int main()
{
    std::thread serverThread(serverFunction);

    // some stuff

    system("pause");

    mu.lock();

    stopServer = true;

    mu.unlock();

    serverThread.join();
}

Why this one example crashes during the run time?

When you leave the inner loop of your thread, you leave the mutex locked, so the parent thread may be blocked forever if you use that mutex again.

You should use std::unique_lock or something similar to avoid problems like that.

You leave your mutex locked. Don't lock mutexes manually in 999/1000 cases.

In this case, you can use std::unique_lock<std::mutex> to create a RAII lock-holder that will avoid this problem. Simply create it in a scope, and have the lock area end at the end of the scope.

{
  std::unique_lock<std::mutex> lock(mu);
  stopServer = true;
}

in main and

  {
    std::unique_lock<std::mutex> lock(mu);
    if (stopServer)
        break;
  }

in serverFunction .

Now in this case your mutex is pointless. Remove it. Replace bool stopServer with std::atomic<bool> stopServer , and remove all references to mutex and mu from your code.

An atomic variable can safely be read/written to from different threads.

However, your code is still busy-waiting. The right way to handle a server processing messages is a condition variable guarding the message queue. You then stop it by front-queuing a stop server message (or a flag) in the message queue.

This results in a server thread that doesn't wake up and pointlessly spin nearly as often. Instead, it blocks on the condition variable (with some spurious wakeups, but rare) and only really wakes up when there are new messages or it is told to shut down.

template<class T>
struct cross_thread_queue {
  void push( T t ) {
    {
      auto l = lock();
      data.push_back(std::move(t));
    }
    cv.notify_one();
  }
  boost::optional<T> pop() {
    auto l = lock();
    cv.wait( l, [&]{ return halt || !data.empty(); } );
    if (halt) return {};
    T r = data.front();
    data.pop_front();
    return std::move(r); // returning to optional<T>, so we'll explicitly `move` here.
  }
  void terminate() {
    {
      auto l = lock();
      data.clear();
      halt = true;
    }
    cv.notify_all();
  }
private:
  std::mutex m;
  std::unique_lock<std::mutex> lock() {
    return std::unique_lock<std::mutex>(m);
  }
  bool halt = false;
  std::deque<T> data;
  std::condition_variable cv;
};

We use boost::optional for the return type of pop -- if the queue is halted, pop returns an empty optional. Otherwise, it blocks until there is data.

You can replace this with anything optional-like, even a std::pair<bool, T> where the first element says if there is anything to return, or a std::unique_ptr<T> , or a std::experimental::optional , or a myriad of other choices.

cross_thread_queue<int> queue;

bool serverFunction()
{
  while (auto message = queue.pop()) {
    // processing *message
    std::cout << "Processing " << *message << std::endl;
  }

  std::cout << "Exiting func..." << std::endl;

  return true;
}

int main()
{
  std::thread serverThread(serverFunction);

  // some stuff
  queue.push(42);

  system("pause");

  queue.terminate();

  serverThread.join();
}

live example .

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