如何放弃或取消std::thread

How to abandon or cancel std::thread

我正在使用 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();
}

这种方法似乎有效,但对我来说却很糟糕。 我不明白为什么线程不会导致访问冲突,因为它试图在对象被销毁后访问它。

有没有办法通知线程它已经分离并且必须在不访问任何 class 成员的情况下退出。

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

上面是ill-formed,因为声明了std::thread::swap

void swap( thread& other ) noexcept;

std::thread(&C::producer, this, 1) 是一个临时值,因此是一个右值。它不能绑定到 swap.

的 non-const 左值引用

也许您打算改写 t1 = std::thread(&C::producer, this, 1);


I don't understand why the thread does not cause an access violation

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

Is this OK

没有

or is there a better way to implement something like this?

理想的解决方案视情况而定。

一个简单的解决方案是使用有状态可调用对象来创建线程,并存储指向共享状态的共享指针。谁活得更长,谁就可以让它活下去。如果经常访问状态,这可能会对性能产生影响。

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

你的线程明明是在你的class C被销毁后访问数据的。这是未定义的行为,所以不要依赖这个

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

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

但是与其分离你正在做的事情,不如加入线程。这将使您的主线程等待,直到您的工作线程在您恢复之前终止。在此之后删除 C 是安全的。

正如接受的答案中所建议的,shared_ptr 可以提供所需的行为。 在 abort-case 中,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();
}