Почему отсоединенный поток в C ++ 11 может выполняться, даже если деструктор был вызван - PullRequest
1 голос
/ 19 июня 2020

Я только что прочитал do c about std::thread.detach() в C ++ 11.

Вот мой тест:

#include <iostream>
#include <thread>
#include <chrono>

static int counter = 0;    

void func()
{
    while (true) {
        std::cout<<"running..."<<std::endl;
        std::cout<<counter++<<std::endl;
        std::this_thread::sleep_for(std::chrono::milliseconds(1000));
    }
}


int main()
{
    {
        std::thread t(func);
        t.detach();
    } // t is released after this line
    // t has died, so who is holding the resources of the detached thread???

    std::cin.get();

    return 0;
}

Этот код работает, как ожидалось. Таким образом, кажется, что поток может продолжать работать, даже если был вызван его деструктор. Верно ли это?

Если это правда, кто вообще владеет ресурсами потока после освобождения объекта t? Есть ли какой-то механизм для удержания ресурсов, например, скрытый анонимный объект?

Ответы [ 3 ]

3 голосов
/ 19 июня 2020

В C ++ std::thread не управляет самим потоком выполнения. В C ++ вообще нет элементов управления для управления потоком выполнения.

std::thread управляет дескриптором потока - идентификатором потока (thread_t в мире Posix, который в основном был модель для std::thread). Такой идентификатор используется для связи (как в control ) с потоком, но в C ++ единственным стандартным способом связи будет join поток (который просто ожидает завершения потока) или detach ing из него.

Когда вызывается деструктор std::thread, дескриптор потока также разрушается, и дальнейшее управление потоком невозможно. Но сам поток выполнения остается и продолжает управляться реализацией (или, точнее, операционной системой).

Обратите внимание, что для неотключенных потоков std::thread деструкторы выдают исключение, если поток не присоединился. Это просто защита от случайной потери разработчиками дескриптора потока, когда они этого не собирались.

1 голос
/ 19 июня 2020

Вы правы, что поток продолжает работать, если он отсоединен после деструктора потока.

Никто на Земле не удерживает ресурсы (если вы не позаботитесь об этом). Однако, когда ваше приложение завершает работу, процесс завершения приложения завершает поток.

Можно по-прежнему организовать связь с отсоединенным потоком и «ждать» его. По сути, join() - это удобный API, поэтому вам не нужно делать что-то вроде этого:

#include <atomic>
#include <chrono>
#include <iostream>
#include <thread>

static int counter = 0;    

std::atomic<bool> time_to_quit{false};
std::atomic<bool> has_quit{false};

void func()
{
    while (!time_to_quit) {
        std::cout<<"running..."<<std::endl;
        std::cout<<counter++<<std::endl;
        std::this_thread::sleep_for(std::chrono::milliseconds(1000));
    }
    has_quit = true;
}


int main()
{
    {
        std::thread t(func);
        t.detach();
    } // t is released after this line

    using namespace std::chrono_literals;
    std::this_thread::sleep_for(3s);
    time_to_quit = true;
    while (!has_quit)
        ;
    std::cout << "orderly shutdown\n";
}
0 голосов
/ 19 июня 2020

Потоки выполнения существуют независимо от объектов потока, которые вы используете для управления ими в C ++. Когда вы отсоединяете объект потока, поток выполнения продолжает работать, но реализация (обычно в сочетании с операционной системой) отвечает за это.

...