У меня вопрос по этому коду, который я хочу запустить в 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 раз, прежде чем перейти к следующей строке кода (последовательной, а не параллельной). Однако, когда я использую режим сна, как в коде, он вообще не печатает никаких чисел и просто переходит к следующей строке кода. Почему не работает спящий режим и как заставить работать такой поток вместо последовательного выполнения?





Примечание 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)
{
}
См. Комментарии в вашем ответе. Он указывает на статью по адресу parashift.com/c++-faq-lite/pointers-to-members.html#faq-33.2, которая подтверждает мое мнение.
Попробуйте сделать идентификатор 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.
Обычно мне проще просто отсоединить и синхронизировать с помощью семафоров.
Здесь уходит по касательной ... Относительно Сообщение Мартина Йорка:
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 и прочтите примечание внизу.
Очаровательный. Таким образом, для компилятора было бы законным иметь дополнительное хранилище, прикрепленное к указателям функций, чтобы указать, указывают ли они на функцию C или C++.
Глупо, увеличивает sizeof (p), ухудшает совместимость с C / C++, добавляет нежелательной боли кодированию и т. д. Но это законно. Спасибо! И спасибо за ссылку! Это хорошо знать!
extern «C» гарантирует, что функция использует ABI «C». Это хорошо определено и стандартно везде. C++ ABI намеренно не определен. Реализация позволяет максимально эффективно определять ABI (это означает, что параметры могут передаваться в регистрах, а не в стеке).
По этой же причине вы не можете связывать объекты, созданные разными компиляторами C++ (поскольку они могут использовать разные ABI). Но независимо от того, какой компилятор C вы используете, ABI всегда будет одинаковым и, следовательно, совместимым.
При реализации C++ многое не указывается. Порядок базовых объектов в производном классе. Расположение указателя vtable. Назовите mangling для реализации перегрузки. И т.д. В C отсутствуют эти функции. Но вы говорите, что мы можем связать вывод любой пары компиляторов C? Цвет меня скептически.
Даже если этого требует ABI, расскажите мне скептически. Я видел, как слишком много людей рассматривают стандарты как не более чем «рекомендуемые руководящие принципы», чтобы когда-либо поверить такому радикальному заявлению. Не говоря уже о проблемах организации файла объекта / библиотеки.
С практической точки зрения, существует очень много решений этих проблем. Это классический принцип ячеек (попробуйте вики). Учитывая ограниченные возможности, в 99,9% случаев автор компилятора делает его совместимым. Выбросы устраняются дарвиновской эволюцией. Но это не обязательно ...
+1 за правильный ответ. Но я НЕ так уверен насчет "отказ от использования статических методов в качестве функции pthread". См .: stackoverflow.com/questions/433220/qnx-c-thread-question#433 614