Подтвердить что ты не робот

Является ли основной поток разрешенным для создания потока POSIX до того, как он войдет в main()?

У меня есть этот объект, содержащий поток. Я хочу, чтобы судьба объекта и судьба нити были едины в одном. Поэтому конструктор создает поток (с pthread_create), и деструктор выполняет действия, чтобы поток возвращался в течение разумного промежутка времени и затем соединял поток. Это работает нормально, пока я не создаю экземпляр одного из этих объектов со статической продолжительностью хранения. Если я создаю экземпляр одного из этих объектов в глобальном пространстве или пространстве имен или в статическом классе, программа компилирует fine (gcc 4.8.1), но сразу же переходит в режим работы. С заявлениями печати я определил, что основной поток даже не вводит main() перед segfault. Любые идеи?

Обновление: также добавлен оператор печати в первую строку конструктора (поэтому до вызова pthread_create), и даже это не печатается перед segfault, но конструктор действительно использует список инициализации, поэтому возможно что-то там вызывает его?

Вот конструктор:

worker::worker(size_t buffer_size):
m_head(nullptr),m_tail(nullptr),
m_buffer_A(operator new(buffer_size)),
m_buffer_B(operator new(buffer_size)),
m_next(m_buffer_A),
m_buffer_size(buffer_size),
m_pause_gate(true),
m_worker_thread([this]()->void{ thread_func(); }),
m_running(true)
{
    print("this wont get printed b4 segfault");
    scoped_lock lock(worker_lock);
    m_worker_thread.start();
    all_workers.push_back(this);
}

И деструктор:

worker::~worker()
{
    {
        scoped_lock lock(worker_lock);
        auto w=all_workers.begin();
        while(w!=all_workers.end())
        {
            if(*w==this)
            {
                break;
            }
            ++w;
        }
        all_workers.erase(w);
    }

    {
        scoped_lock lock(m_lock);
        m_running=false;
    }

    m_sem.release();
    m_pause_gate.open();

    m_worker_thread.join();

    operator delete(m_buffer_A);
    operator delete(m_buffer_B);
}

Обновление 2:

Хорошо, я понял это. Моя функция печати является атомарной, а также защищает cout с мьютексом области внешнего пространства имен, определенным в другом месте. Я изменился до простого cout, и он был напечатан в начале ctor. По-видимому, ни один из этих мьютексов продолжительности статического хранения не инициализируется, прежде чем что-то пытается получить к ним доступ. Так что, вероятно, это ответ Кейси.

Я просто не буду беспокоиться о сложных объектах и ​​продолжительности статического хранения. В любом случае это не имеет большого значения.

4b9b3361

Ответ 1

Инициализация нелокальных переменных описана в С++ 11 §3.6.2, там тонна страшного материала в параграфе 2, связанная с потоками:

Если программа запускает поток (30.3), последующая инициализация переменной не зависит от инициализации переменной, определенной в другой единицы перевода. В противном случае инициализация переменной неопределенно секвенируется относительно инициализации переменной, определенной в другой единицы перевода. Если программа запускает поток, последующая неупорядоченная инициализация переменной не зависит от каждой другой динамической инициализации.

Я интерпретирую "последующая неупорядоченная инициализация переменной не зависит от каждой другой динамической инициализации" означает, что порожденный поток не может получить доступ к любой переменной с динамической инициализацией, которая не была инициализирована до того, как поток был порожден, не вызывая гонки данных, Если этот поток никоим образом не синхронизируется с main, вы в основном танцуете на минном поле руками своими глазами.

Я настоятельно рекомендую вам прочитать и понять все 3,6; даже без потоков это огромный PITA, чтобы сделать многое до начала main.

Ответ 2

Что произойдет, прежде чем вводить main будет специфичным для платформы, но вот ссылка на то, как main() выполняется в Linux

http://linuxgazette.net/84/hawk.html

Полезный снайпер

__ libc_start_main инициализирует необходимые материалы, особенно библиотеку C (например, malloc) и среду потоков, и вызывает нашу основную.

Для получения дополнительной информации найдите __libc_start_main

Не уверен, как это ведет себя в Windows, но кажется, что любой стандартный вызов библиотеки C перед входом на главную не является хорошей идеей.

Ответ 3

Там может быть много способов сделать это. См. Фрагмент ниже, где конструктор класса A называется до main, потому что мы объявили объект класса A в глобальной области: (я расширил этот пример, чтобы продемонстрировать, как поток может быть создан до того, как выполняется main)

#include <iostream>
#include <stdlib.h>
#include <pthread.h>
using namespace std;

void *fun(void *x)
{
    while (true) {
        cout << "Thread\n";
        sleep(2);
    }
}

pthread_t t_id;
class A
{
    public: 
        A() 
        { 
            cout << "Hello before main \n " ;
            pthread_create(&t_id, 0, fun, 0);
            sleep(6);
        }
};

A a;
int main()
{
    cout << "I am main\n";
    sleep(40);
    return 0;
}