2017-02-14 530 views
0

我有两个问题。如何说std :: thread停止?

1)我想用无限循环启动一些函数,像服务器一样工作,并在单独的线程中检查消息。但是,我想在我想要时从父线程关闭它。我很困惑如何在这种情况下std::futurestd::condition_variable。或者是创建一些全局变量并将它从父线程更改为true/false更好。
2)我想要这样的东西。为什么这个例子在运行时崩溃?

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

std :: atomic stopServer;'应该就够了。 –

回答

4

在运行时为什么这样一个例子崩溃?

当您离开线程的内部循环时,会将互斥锁保持锁定状态,因此如果再次使用该互斥锁,父线程可能永远被阻塞。

您应该使用std::unique_lock或类似的东西来避免这样的问题。

+0

那么解决方案呢?这是一个好的吗? – banana36

+0

我认为这种情况并没有出现,因为互斥锁只在主服务器和内部的服务器中获得并释放,只有在主服务器已经发生的情况下才会释放。 – Skym0sh0

+0

@ banana36您的解决方案是可以的,但是 - 正如其中一位评论者指出的那样 - 您应该使用std :: atomic变量。您使用的方法非常普遍。 –

1

您将锁定互斥体。不要在999/1000的情况下手动锁定互斥锁。

在这种情况下,您可以使用std::unique_lock<std::mutex>创建一个RAII锁定器,以避免此问题。只需在示波器中创建它,并将锁定区域结束于示波器的末端。

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

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

现在在这种情况下,你的互斥量是毫无意义的。去掉它。将bool stopServer替换为std::atomic<bool> stopServer,并从您的代码中删除对mutexmu的所有引用。

可以安全地从不同线程读取/写入原子变量。

但是,您的代码仍在等待。处理服务器处理消息的正确方法是守护消息队列的条件变量。然后通过在消息队列中对停止服务器消息(或标志)进行排队来停止它。

这会导致一个服务器线程无法唤醒,并且几乎无时无刻地旋转。相反,它阻塞条件变量(带有一些虚假的唤醒,但很少见),并且只有在有新消息或被告知关闭时才真正醒来。

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

我们使用boost::optionalpop返回类型 - 如果队列被暂停,弹出返回一个空可选。否则,它会阻塞,直到有数据。

您可以用任意类似的东西,甚至是第一个元素说明是否有任何东西要返回的东西,或std::unique_ptr<T>std::experimental::optional或其他无数种选择来替换它。

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