如何放弃或取消std :: thread

时间:2019-07-15 16:46:16

标签: c++ multithreading stdthread

我正在使用std :: threads实现生产者,该生产者会进行一些长时间的计算,但是所使用的代码并未提供中止方法。但是我需要调用线程在请求​​中止后立即继续前进。 所以我试图通过调用detach()放弃线程。这个想法是线程完成了计算,但是结果却被丢弃了:

#include <thread>
#include <iostream>
#include <queue>
#include <mutex>

class C
{
public:
    void spawn()
    {
        t1.swap(std::thread(&C::producer, this, 1));
    }


    void abort()
    {
        std::lock_guard<std::mutex> lk(mx);
        aborted = true;
        t1.detach();
    }

    bool tryGetResult(int &result)
    {
        std::lock_guard<std::mutex> lk(mx);
        if (!q.empty()) {
            result = q.front();
            q.pop();
            return true;
        }
        else
            return false;
    }

private:
    std::thread t1;
    std::mutex mx;
    std::queue<int> q;
    bool aborted = false;

    void producer(int id)
    {
        int i = 0;
        while (true) {
            std::lock_guard<std::mutex> lk(mx);
            if (aborted) break;
            q.push(id * 10 + i);
            i++;
        }
    }
};

int main()
{
    {
        C c;
        c.spawn();
        std::this_thread::sleep_for(std::chrono::milliseconds(2000));
        c.abort();
    }

    std::cout << "Done...";
    std::cin.get();
}

这种方法似乎奏效,但对我来说却闻起来。 我不明白为什么该线程不会引起访问冲突,因为它在对象被销毁后会尝试访问该对象。

是否有一种方法可以通知线程它已被分离,并且必须退出而不访问任何类成员。

3 个答案:

答案 0 :(得分:1)

t1.swap(std::thread(&C::producer, this, 1));

上面的格式不正确,因为声明了std::thread::swap

void swap( thread& other ) noexcept;

std::thread(&C::producer, this, 1)是一个临时值,因此是一个右值。不能将其绑定到swap的非常量左值引用。

也许您确实想写t1 = std::thread(&C::producer, this, 1);


  

我不明白为什么该线程不会引起访问冲突

在对象生命周期之外访问对象的行为是不确定的。不保证会导致访问冲突。

  

可以吗

否。

  

还是有更好的方法来实现这样的东西?

理想的解决方案取决于具体情况。

一个简单的解决方案是使用有状态的可调用对象创建线程,并存储指向共享状态的共享指针。寿命更长的人都可以保持生命。如果经常访问状态,这可能会对性能产生影响。

更简单的解决方案是将静态存储用于共享状态。这没有共享指针可能存在的潜在性能问题,但是全局状态还存在其他问题。

答案 1 :(得分:0)

在销毁C类之后,线程将清楚地访问数据。这是未定义的行为,因此请勿依赖

如果我对您的理解正确,那么您希望能够在退出程序之前终止线程。这本质上是不安全的,因为您不知道线程在终止时正在访问哪个内存,并且您的程序很可能会处于错误状态。

终止线程的唯一正确方法是关闭线程本身。您已经在中止功能中执行了此操作。这会将aborted变量设置为true,这反过来会破坏您的线程循环并退出线程功能。

但是,与其执行分离操作,不如加入它,而应该加入线程。这将使您的主线程等到您的工作线程终止后再继续。在此之后,可以安全删除C。

答案 2 :(得分:0)

如已接受的答案中所建议,shared_ptr可以提供所需的行为。 在中止情况下,shared_ptr的生存时间与最后一个废弃线程的生存时间一样。

#include <thread>
#include <iostream>
#include <queue>
#include <mutex>
#include <memory>

class C
{
public:
    ~C()
    {
        std::cout << "Dtor\n";
    }


    void abort()
    {
        std::lock_guard<std::mutex> lk(mx);
        aborted = true;
    }

    std::mutex mx;
    std::queue<int> q;
    bool aborted = false;

    static void staticProducer(std::shared_ptr<C> arg, int id)
    {
        arg->producer(id);
    }

    void producer(int id)
    {
        int i = 0;
        while (i<300000) {
            std::lock_guard<std::mutex> lk(mx);
            if (aborted) break;
            q.push(id * 10 + i);
            i++;
            std::cout << "Push\n";
        }
    }
};

int main()
{
    {
        std::shared_ptr<C> c = std::make_shared<C>();
        std::thread t1 = std::thread(&C::staticProducer, c, 1);

        std::this_thread::sleep_for(std::chrono::milliseconds(2000));

        // scenario 1: abort
        c->abort();
        t1.detach();

        // scenario 2: wait for completion and get result
        // t1.join();
        // std::cout << c->q.size() << "\n";
    }

    std::cout << "Done...";
    std::cin.get();
}