c++ - 类中的 C++ 后台线程 - 实例范围
问题描述
我有这个简单的课程:
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 也会超出范围。如果我foo
通过 更改为对象new
,它将运行,但会导致内存泄漏。
我正在考虑使用以下方法创建析构函数:
~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
}
}
推荐阅读
- marklogic - “ml-gradle”> v3.4.1 中的 401 未授权问题
- java - 如何在android studio中填充excel工作表数据并以给定应用程序的工作表格式获取日期数据
- git - 如何下载 Heroku 蛞蝓?
- python - Pytorch unet 语义分割
- karate - karate-gatling:当 gatling 馈线和模拟工作流程由于登录请求错误而失败时,不会生成报告
- android - 水平 RecylerView 未正确显示在多行中
- python - 如何在 plotly-dash 应用程序中绘制图形
- c++ - Visual Studio 无法显示 std::string 的值
- javascript - 在Typescript或Javascript中按数字然后字母对对象键进行排序
- mysql - 存储过程未在 codeigniter 中返回结果