Функция Boost :: Thread, приводящая к ошибке сегментации во встроенном ARM - PullRequest
2 голосов
/ 20 января 2012

У меня странная проблема с многопоточным классом, использующим Boost :: threads. Вот краткое резюме того, что я делаю:

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

Затем я вызываю метод класса обработчика (startUpdate), который создает новый экземпляр моего поточного класса. Вот код класса с резьбой:

class Sensor_Thread
{
  public:
    //constructor (creates thread and binds the update function to it
    Sensor_Thread (const Ptr<Sensor_Base> & theSensor): m_stoprequested (false),
                      s (theSensor),
                      m_thread (boost::bind (&Sensor_Thread::update, this)) { }
    //default null constructor, shouldn't ever be used
    Sensor_Thread (): m_stoprequested (true),
                      m_thread (),
                      s (NULL) { }

    //destructor (automatically joins the thread as per RAII principles)
    ~Sensor_Thread () { m_stoprequested = true; m_thread.join (); }

  private:
    volatile bool m_stoprequested;
    boost::mutex m_mutex;
    boost::thread m_thread;
    Ptr<Sensor_Base> s;

    void update ();

};

(класс "Ptr" - это мой класс общего указателя ... Я вполне уверен, что он работает правильно, потому что я изначально получил его из учебника по C ++ ...)

Функция обновления:

void Sensor_Thread::update ()
{
  //make sure we actually have a sensor attached...
  if (s) { 
    // set up structure for sleeping
    struct timespec time;
    while (!m_stoprequested)
    {
      boost::mutex::scoped_lock lock(m_mutex);
      s->update ();
      time.tv_sec = s->updateInterval / 1000;
      time.tv_nsec = (1000 % s->updateInterval) * (1000 * 1000);
      nanosleep (&time, NULL);
    }
  }
}

Это выполняется бесконечно, пока другой триггер в драйвере не вызовет stopUpdate и не будет уничтожен threadaded_class.

Странность: На моем компьютере для разработки, который является OS X 10.6, с использованием darwin gcc 4.2.1, он работает нормально, именно так, как и ожидалось.

Это предназначено для запуска на встроенном сервере с Debian Linux и процессором ARM. У меня есть набор инструментов для кросс-компиляции, предоставленный производителем встроенной системы, и когда я использую его для кросс-компиляции, я получаю ошибку сегмента. Благодаря отладке я обнаружил, что эта ошибка сегмента возникает, когда вызывается s-> update () (или ЛЮБАЯ другая попытка разыменовать общий указатель и что-то с ним сделать). Однако, если я введу небольшую задержку, скажем, добавив «sleep (1);» до запуска цикла while в моей функции Sensor_Thread :: update он работает безупречно.

По-моему, это означает, что система пытается разыменовать общие указатели до того, как они будут полностью или адекватно инициализированы? Обход сна (1) заставляет его работать, но, тем не менее, это кажется мне очень странным. Если разделяемый указатель поточного класса инициализируется во время конструктора, разве он не должен быть готов до того, как функция обновления когда-либо будет вызвана? Или создание boost :: thread означает, что функция обновления происходит одновременно с инициализацией общего указателя, принадлежащего многопоточному классу? Есть ли более чистый способ, чем "спящий" взлом, чтобы сделать SURE инициализированным указателем перед вызовом функции обновления?

Спасибо !!!

1 Ответ

3 голосов
/ 20 января 2012
Sensor_Thread (const Ptr<Sensor_Base> & theSensor): m_stoprequested (false),
                  s (theSensor),
                  m_thread (boost::bind (&Sensor_Thread::update, this)) { }

Этот код не работает.Вы звоните update на объект, который еще не построен.Использование this в списке инициализации конструктора всегда должно поднимать красный флаг.Это указатель на объект, который еще не существует полностью.

Обычный способ справиться с этим - разделить его на два этапа.Есть метод run или start, который создает поток.Вызовите этот метод после возврата конструктора.

while (!m_stoprequested)
{
  boost::mutex::scoped_lock lock(m_mutex);
  s->update ();
  time.tv_sec = s->updateInterval / 1000;
  time.tv_nsec = (1000 % s->updateInterval) * (1000 * 1000);
  nanosleep (&time, NULL);
}

Возможно, это не то, что вам нужно.Он содержит мьютекс всегда , что затрудняет доступ другого потока к s.Придется подождать до следующего обновления и затем выиграть гонку за мьютексом.На некоторых платформах потоку, выполняющему настоящую работу, будет трудно превзойти «интерактивный» поток (который в основном спит).Так что это может замедлить любой другой поток, который пытается получить значительный доступ к s.

Почему вы вызываете nanosleep, удерживая мьютекс?

...