std :: thread vs pthead,我在做什么错?

时间:2019-07-19 05:14:45

标签: c++ multithreading lambda pthreads stdthread

我正在研究一个项目,该项目需要在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类的一种方法,可以确保在生成的线程终止之前不会超出范围,而且我使用的变量(timeOutactiveId都是对象)


编辑: 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)

2 个答案:

答案 0 :(得分:2)

在线程中,您正在访问对int *t方法本地的变量std::string *sContainerManager::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将与其他任何线程完全分离。当然,这意味着没有其他地方的数据指针或引用,请确保您确实在制作timeOutactiveId的完整副本。

或者,我建议将线程存储为类的数据成员,而不是detach。然后,在析构函数中使用join。如果线程较早完成,则join基本上是无操作的。如果线程未完成,则将阻止线程使用的资源超出范围,直到线程完成。这将解决超出范围的变量,但不会解决任何竞争条件。竞态条件可以通过使用std::atomic或互斥体来解决。

由于第二个解决方案(使用joinstd::atomic和/或互斥锁)比较复杂,并且需要检查生存期和竞争条件,因此我建议使用第一个解决方案(使用不具有捕获任何东西,所有参数都通过副本传递)。