繁体   English   中英

class 中的 C++ 后台线程 - 实例 scope

[英]C++ background thread in class - instance scope

我有这个简单的 class:

struct Foo {        
    void Run() {
        this->bgLoader = std::thread([this]() mutable {
            //do something 

            this->onFinish_Thread();
        });
    }
    
    std::function<void()> onFinish_Thread;
    std::thread bgLoader;
};

这是从 C-API 调用的:

void CApiRunFoo(){
    Foo foo;
    foo.onFinish_Thread = []() {
        //do something at thread end
    };
    foo.Run();
}

我想运行CApiRunFoo ,从它返回但保持线程运行直到它完成。

现在,问题是,一旦CApiRunFoo结束,即使后台线程仍在运行, foo 也会退出 scope 。 如果我通过newfoo更改为 object ,它将运行,但会导致 memory 泄漏。

我正在考虑使用以下方法创建析构函数:

~Foo(){
    if (bgLoader.joinable()){
        bgLoader.join();
    }
}

但我不确定它是否会导致死锁,而且它可能不会导致CApiRunFoo返回,直到线程完成。

这个问题有什么解决方案/设计模式吗?

您可以将Foo实例返回给 C 程序:

struct Foo {        
    ~Foo() {
        if (bgLoader.joinable()) {
            run = false;
            bgLoader.join();
        }
    }
    void Run() {
        run = true;
        this->bgLoader = std::thread([this]() mutable {
            while(run) {
                // do stuff
            }

            this->onFinish_Thread();
        });
    }
    std::atomic<bool> run;
    std::function<void()> onFinish_Thread;
    std::thread bgLoader;
};

C接口:

extern "C" {

struct foo_t {
    void* instance;
};

foo_t CApiRunFoo() {
    Foo* ptr = new Foo;
    ptr->onFinish_Thread = []() {
        std::cout << "done\n";
    };
    ptr->Run();
    return foo_t{ptr};
}

void CApiDestroyFoo(foo_t x) {
    auto ptr = static_cast<Foo*>(x.instance);
    delete ptr;
}

}

还有一个 C 程序:

int main() { 
    foo_t x = CApiRunFoo();

    CApiDestroyFoo(x);
}

演示


看起来您希望Foo对象在线程完成时自动自毁,您可以分离运行它们并让它们delete this; 完成后。

#include <atomic>
#include <condition_variable>
#include <cstdint>
#include <iostream>
#include <functional>
#include <mutex>
#include <thread>

// Counting detached threads and making sure they are all finished before
// exiting the destructor. Used as a `static` member of `Foo`.
struct InstanceCounter {
    ~InstanceCounter() {
        run = false;
        std::unique_lock lock(mtx);
        std::cout << "waiting for " << counter << std::endl;
        while(counter) cv.wait(lock);
        std::cout << "all done" << std::endl;
    }
    void operator++() {
        std::lock_guard lock(mtx);
        std::cout << "inc: " << ++counter << std::endl;
    }
    void operator--() {
        std::lock_guard lock(mtx);
        std::cout << "dec: " << --counter << std::endl;
        cv.notify_one();      // if the destructor is waiting
    }
    std::atomic<bool> run{true};
    std::mutex mtx;
    std::condition_variable cv;
    unsigned counter = 0;
};

struct Foo {      
    bool Run() {        
        try {
            ++ic; // increase number of threads in static counter
            bgLoader = std::thread([this]() mutable {
                while(ic.run) {
                    // do stuff
                }
                // if onFinish_Thread may throw - you may want to try-catch:
                onFinish_Thread();
                --ic; // decrease number of threads in static counter       
                delete this; // self destruct
            });
            bgLoader.detach();
            return true;  // thread started successfully
        }
        catch(const std::system_error& ex) {
            // may actually happen if the system runs out of resources
            --ic;
            std::cout << ex.what() << ": ";
            delete this;
            return false; // thread not started
        }
    }

    std::function<void()> onFinish_Thread;

private:
    ~Foo() { // private: Only allowed to self destruct
        std::cout << "deleting myself" << std::endl;
    }

    std::thread bgLoader;
    static InstanceCounter ic;
};

InstanceCounter Foo::ic{};

现在 C 界面变得更像您在问题中所拥有的。

#include <stdbool.h>

extern "C" {

bool CApiRunFoo() {
    Foo* ptr = new Foo;
    ptr->onFinish_Thread = []() {
        std::cout << "done" << std::endl;
    };
    return ptr->Run();
    // it looks like `ptr` is leaked here, but it self destructs later
}

}

演示

您的程序应该在将来的某个时候调用join并完成新线程(另请参见这个问题和答案)。 为此,它应该持有对线程 object 的引用(在广义上)。 在您当前的设计中,您的foo就是这样的参考。 所以你不能失去它。

您应该考虑一个有意义的地方join您的代码。 这同一个地方应该保存你的foo 如果你这样做,没有问题,因为foo还包含onFinish_Thread object。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM