Вопрос по теме QNX c ++ - PullRequest
3 голосов
/ 11 января 2009

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

Ответы [ 3 ]

4 голосов
/ 11 января 2009

Примечание 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's является библиотекой C и поэтому ожидает указатель на функцию с C ABI. Вам просто повезло с вашей платформой здесь. Вы должны определить это как функцию и объявить ABI, используя extern "C"

// This needs to be a standard function with C Interface.
extern "C" void *start_routine_trampoline(void *p)
{
}
3 голосов
/ 11 января 2009

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

Технически это не является утечкой ресурсов (если поток специально не присоединяется). И присоединение позволит избежать проблемы, описанной Martin York .

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

Присоединенный поток th должен находиться в состоянии соединения: он не должен иметь был отключен с помощью pthread_detach (3) или PTHREAD_CREATE_DETACHED атрибут pthread_create (3).

Когда завершается присоединяемый поток, его ресурсы памяти (поток дескриптор и стек) не освобождаются, пока другой поток не выполнит pthread_join на это. Следовательно, pthread_join должен быть вызван один раз для каждый присоединяемый поток создан, чтобы избежать утечек памяти.

0 голосов
/ 11 января 2009

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

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

// Это должна быть стандартная функция с интерфейсом C.
extern "C" void * start_routine_trampoline (void * p) {...}

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

(1) C ++ был спроектирован так, чтобы быть максимально совместимым с C. Есть несколько отличий ... Но у меня сложилось впечатление, что extern "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);
}
...