Вопрос по теме QNX C++

У меня вопрос по этому коду, который я хочу запустить в QNX:

class ConcreteThread : public Thread
{
public:
    ConcreteThread(int test)
    {
        testNumber = test;
    }

    void *start_routine() 
    { 
        for(int i = 0; i < 10; i++)
        {
            sleep(1);
            cout << testNumber << endl;
        }   
    }

private:
    int testNumber;
};




class Thread 
{
public:
    Thread(){};

    int Create()
    {
        pthread_t m_id;
        return pthread_create(&m_id, NULL, &(this->start_routine_trampoline), this);
    }

protected:
    virtual void *start_routine() = 0;

private:

    static void *start_routine_trampoline(void *p)
    {
        Thread *pThis = (Thread *)p;
        return pThis->start_routine();
    }
};

Теперь, когда я запускаю этот код без сна в * start_routine, он просто выводит число 10 раз, прежде чем перейти к следующей строке кода (последовательной, а не параллельной). Однако, когда я использую режим сна, как в коде, он вообще не печатает никаких чисел и просто переходит к следующей строке кода. Почему не работает спящий режим и как заставить работать такой поток вместо последовательного выполнения?

Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
3
0
3 710
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Ответ принят как подходящий

Примечание 1. Если у вас только 1 процессор, код может выполняться только последовательно, независимо от того, сколько потоков вы создаете. Каждому потоку дается часть процессорного времени, прежде чем он будет заменен для следующих потоков.

Примечание 2: если основной поток завершает работу, pthreads убивает все дочерние потоки до того, как у них будет возможность выполнить.

Теперь отвечу на ваши вопросы:

Без сна. Когда-то запущенный поток имеет достаточно времени в единственном срезе, которое ему было предоставлено, чтобы выполнить цикл 10 раз полностью.

Со сном: ваш рабочий поток будет спать на целую секунду. Итак, у вашего основного потока есть время для большой работы. Если основной поток завершится в это время, рабочий будет убит.

Я бы внес следующие изменения:

//  Remove the Create() method
//  Put thread creation in the constructor.
//  Make the thread variable part of the object

pthread_t m_id;

Thread()
{
    if (pthread_create(&m_id, NULL, &(this->start_routine_trampoline), this) != 0)
    {
        throw std::runtime_error("Thread was not created");
    }
}

// Make sure the destructor waits for the thread to exit.
~Thread()
{
    pthread_join(m_id);
}

Если вы пойдете и посмотрите библиотеку увеличить поток. вы обнаружите, что все подобные мелкие ошибки уже устранены; Таким образом упрощается использование потоковой передачи.

Также обратите внимание. Использование статики может работать, но не переносимо. Это связано с тем, что pthread является библиотекой C и, таким образом, ожидает указатель на функцию с C ABI. Вам просто повезло с вашей платформой. Вам нужно определить это как функцию и объявить ABI, используя extern "C"

// This needs to be a standard function with C Interface.
extern "C" void *start_routine_trampoline(void *p)
{
}

+1 за правильный ответ. Но я НЕ так уверен насчет "отказ от использования статических методов в качестве функции pthread". См .: stackoverflow.com/questions/433220/qnx-c-thread-question#433‌ 614

Mr.Ree 11.01.2009 23:32

См. Комментарии в вашем ответе. Он указывает на статью по адресу parashift.com/c++-faq-lite/pointers-to-members.html#faq-33.2, которая подтверждает мое мнение.

Martin York 12.01.2009 10:31

Попробуйте сделать идентификатор pthread_t членом класса вместо локальной переменной функции. Таким образом вызывающий может присоединиться к pthread_join.

Невыполнение этого технически является утечкой ресурсов (если только поток специально не может быть присоединен). А присоединение позволит избежать проблемы, описанной Мартин Йорк.

От человека pthread_join:

The joined thread th must be in the joinable state: it must not have been detached using pthread_detach(3) or the PTHREAD_CREATE_DETACHED attribute to pthread_create(3).

When a joinable thread terminates, its memory resources (thread descriptor and stack) are not deallocated until another thread performs pthread_join on it. Therefore, pthread_join must be called once for each joinable thread created to avoid memory leaks.

Обычно мне проще просто отсоединить и синхронизировать с помощью семафоров.

Mr.Ree 11.01.2009 23:33

Здесь уходит по касательной ... Относительно Сообщение Мартина Йорка:

Also note. That using a static may work but it is non portable. This is because pthread's is a C library and is thus expecting a function pointer with a C ABI. You are just getting lucky for your platform here. You need to define this as a function and declare the ABI by using extern "C"

// This needs to be a standard function with C Interface.
extern "C" void * start_routine_trampoline(void * p) {...}

Я не уверен в этом ...

(1) C++ был разработан так, чтобы быть максимально совместимым с C. Есть несколько отличий ... Но у меня сложилось впечатление, что внешний "C"   использовался в основном для обхода изменения имен, необходимого для реализации перегрузки функций C++.

(2) Похоже, что когда у вас есть указатель на функцию, соглашения о вызовах (то, что помещается в стек для вызова функции) должны быть одинаковыми для C и C++. В противном случае, как бы работали указатели на функции?

Например.:

Код C:

void bar( int i ) { printf( "bar %d\n", i ); }

Код C++:

class Foo
{
public:
  static void foo( int i ) { cout << "foo " << i << endl; }
};

extern "C" { void bar(int); }

int main()
{
  void (*p)(int);

  p = & Foo::foo;
  (*p)(1);

  p = & bar;
  (*p)(2);
}

Функции статического класса не имеют гарантий по соглашениям о вызовах, они просто обычно реализуются таким образом, который совместим с соглашением C: см. parashift.com/c++-faq-lite/pointers-to-members.html#faq-33.2 и прочтите примечание внизу.

Evan Teran 12.01.2009 07:51

Очаровательный. Таким образом, для компилятора было бы законным иметь дополнительное хранилище, прикрепленное к указателям функций, чтобы указать, указывают ли они на функцию C или C++.

Mr.Ree 12.01.2009 08:01

Глупо, увеличивает sizeof (p), ухудшает совместимость с C / C++, добавляет нежелательной боли кодированию и т. д. Но это законно. Спасибо! И спасибо за ссылку! Это хорошо знать!

Mr.Ree 12.01.2009 08:02

extern «C» гарантирует, что функция использует ABI «C». Это хорошо определено и стандартно везде. C++ ABI намеренно не определен. Реализация позволяет максимально эффективно определять ABI (это означает, что параметры могут передаваться в регистрах, а не в стеке).

Martin York 12.01.2009 10:20

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

Martin York 12.01.2009 10:33

При реализации C++ многое не указывается. Порядок базовых объектов в производном классе. Расположение указателя vtable. Назовите mangling для реализации перегрузки. И т.д. В C отсутствуют эти функции. Но вы говорите, что мы можем связать вывод любой пары компиляторов C? Цвет меня скептически.

Mr.Ree 16.01.2009 01:50

Даже если этого требует ABI, расскажите мне скептически. Я видел, как слишком много людей рассматривают стандарты как не более чем «рекомендуемые руководящие принципы», чтобы когда-либо поверить такому радикальному заявлению. Не говоря уже о проблемах организации файла объекта / библиотеки.

Mr.Ree 16.01.2009 01:51

С практической точки зрения, существует очень много решений этих проблем. Это классический принцип ячеек (попробуйте вики). Учитывая ограниченные возможности, в 99,9% случаев автор компилятора делает его совместимым. Выбросы устраняются дарвиновской эволюцией. Но это не обязательно ...

Mr.Ree 16.01.2009 01:51

Другие вопросы по теме