我正在研究一个项目,该项目需要在docker容器内执行某些过程。我要处理流程未按时终止的情况(比如说在10 s之内)。
我正在使用这个DockerClientpp库来管理基本上只是向Docker套接字发出HTTP请求的容器。到目前为止,一切都很好。
要停止耗时太长的容器,我正在使用单独的线程。问题是我可以使用 ptheads 来实现它,但是我找不到使用 std :: thread 和 lambas
的方法这是我使用pthread的有效实现
void *ContainerManager::spawnKiller(void *ref) {
ContainerManager *self = (ContainerManager *)ref;
std::unique_ptr<DockerClientpp::DockerClient> dc(new DockerClientpp::DockerClient());
std::cout << "[slave]forceStop(): Waiting " << self->timeOut << " before stopping " << self->activeId << std::endl;
std::this_thread::sleep_for(std::chrono::seconds(self->timeOut));
try {
dc->stopContainer(self->activeId);
std::cout << "[slave]forceStop(): Container will be force-stopped" << std::endl;
} catch(std::exception &e) {
// container has already been destroyed
std::cout << "[slave]forceStop(): Error => " << e.what() << std::endl;
}
pthread_exit(0);
}
void ContainerManager::execute() {
pthread_t killerId;
pthread_create(&killerId, nullptr, &(ContainerManager::spawnKiller), (void *)this);
pthread_detach(killerId);
}
这是我的std :: thread和lambda实现,当我尝试分离线程时,该实现因SEGFAULT而失败。
void ContainerManager::execute() {
std::thread([this]() {
std::this_thread::sleep_for(std::chrono::seconds(timeOut));
try {
dc->stopContainer(activeId);
std::cout << "[slave]forceStop(): Container will be force-stopped" << std::endl;
} catch(std::exception &e) {
// container has already been destroyed
std::cout << "[slave]forceStop(): Error => " << e.what() << std::endl;
}
}).detach();
}
这就是gdb所显示的
Thread 1 "test" received signal SIGSEGV, Segmentation fault.
0x0000000000000000 in ?? ()
(gdb) bt
#0 0x0000000000000000 in ?? ()
#1 0x00000000007c6801 in std::thread::detach() ()
#2 0x0000000000410785 in ContainerManager::execute (this=0x7fffffffe2a0, processName=...)
at ../container_manager.cpp:223
#3 0x0000000000412c99 in ContainerManager::executeNew (this=0x7fffffffe2a0, processName=...,
replace=false, language=@0x7fffffffe020: ContainerManager::GO) at ../container_manager.cpp:336
#4 0x00000000004094a9 in main () at test.cpp:36
我尝试使用常规函数而不是lamba,尝试捕获参数,还尝试将参数作为参数传递,但是遇到问题。
我还没有尝试使用new thread(...)
动态分配线程,但是据我了解,即使std::thread
变量超出范围,该线程仍然有效。
您对我做错了什么建议吗?我觉得我真的缺少有关std :: thread和lambda的东西。
execute方法是ContainerManager类的一种方法,可以确保在生成的线程终止之前不会超出范围,而且我使用的变量(timeOut
和activeId
都是对象)
编辑:
detach()
如果我运行此
void ContainerManager::execute() {
int *t = new int;
*t = timeOut;
std::string *s = new std::string;
*s = activeId;
std::thread x([&t, &s]() {
std::cout << "LOL" << std::endl;
std::this_thread::sleep_for(std::chrono::seconds(*t));
std::unique_ptr<DockerClientpp::DockerClient> _dc(new DockerClientpp::DockerClient());
try {
_dc->stopContainer(*s);
std::cout << "[slave]forceStop(): Container will be force-stopped" << std::endl;
} catch(std::exception &e) {
// container has already been destroyed
std::cout << "[slave]forceStop(): Error => " << e.what() << std::endl;
}
});
std::cout << "Detaching" << std::endl;
if(x.joinable()) {
std::cout << ".. in a moment" << std::endl;
x.detach();
}
}
我得到这个输出
Detaching
.. in a moment
Segmentation fault (core dumped)
答案 0 :(得分:2)
在线程中,您正在访问对int *t
方法本地的变量std::string *s
和ContainerManager::execute()
的引用。 ContainerManager::execute()
完成后,对两个变量的访问将导致未定义的行为,在您的情况下将导致SEGFAULT。而是将每个值的两个指针传递给lamdba(甚至更好:根本不使用new
):
void ContainerManager::execute() {
int *t = new int;
*t = timeOut;
std::string *s = new std::string;
*s = activeId;
std::thread x([t, s]() { // <<--- Pass by value
std::cout << "LOL" << std::endl;
std::this_thread::sleep_for(std::chrono::seconds(*t));
std::unique_ptr<DockerClientpp::DockerClient> _dc(new DockerClientpp::DockerClient());
try {
_dc->stopContainer(*s);
std::cout << "[slave]forceStop(): Container will be force-stopped" << std::endl;
} catch(std::exception &e) {
// container has already been destroyed
std::cout << "[slave]forceStop(): Error => " << e.what() << std::endl;
}
});
std::cout << "Detaching" << std::endl;
if(x.joinable()) {
std::cout << ".. in a moment" << std::endl;
x.detach();
}
}
答案 1 :(得分:1)
segfault对我来说,该类超出了范围,即使您希望不会出现这种情况。另一种可能性是,您正在访问的变量上出现竞争条件。
与其在lambda中捕获this
,不如尝试通过拷贝将 all 变量传递给lambda。这将消除与范围有关的任何竞争条件,并解决所有潜在的生命周期问题,因为lambda将与其他任何线程完全分离。当然,这意味着没有其他地方的数据指针或引用,请确保您确实在制作timeOut
和activeId
的完整副本。
或者,我建议将线程存储为类的数据成员,而不是detach
。然后,在析构函数中使用join
。如果线程较早完成,则join
基本上是无操作的。如果线程未完成,则将阻止线程使用的资源超出范围,直到线程完成。这将解决超出范围的变量,但不会解决任何竞争条件。竞态条件可以通过使用std::atomic
或互斥体来解决。
由于第二个解决方案(使用join
,std::atomic
和/或互斥锁)比较复杂,并且需要检查生存期和竞争条件,因此我建议使用第一个解决方案(使用不具有捕获任何东西,所有参数都通过副本传递)。