Переносимый класс потоков реализован на C ++ - PullRequest
3 голосов
/ 24 февраля 2011

При написании программ на C, которым необходимо совместно использовать переменную области файла между приложением и подпрограммой прерывания / потоком / подпрограммой обратного вызова, хорошо известно, что переменная должна быть объявлена ​​как volatile, иначе компилятор может сделать неверный оптимизаций. Это пример того, что я имею в виду:

int flag;

void some_interrupt (void)
{
  flag = 1;
}



int main()
{
  flag = 0;
  ...

  /* <-- interrupt occurs here */

  x = flag; /* BUG: the compiler doesn't realize that "flag" was changed 
                    and sets x to 0 even though flag==1 */

}

Чтобы предотвратить вышеуказанную ошибку, «flag» должен был быть объявлен как volatile.

Мой вопрос: как это применимо к C ++ при создании класса, содержащего поток?

У меня есть класс, который выглядит примерно так:

class My_thread
{
  private:
    int flag;

    static void thread_func (void* some_arg) // thread callback function
    {
      My_thread* this_ptr= (My_thread*)some_arg;

    }
};

«some_arg» будет содержать указатель на экземпляр класса, так что каждый объект «My_thread» имеет свой собственный поток. Через этот указатель он получит доступ к переменным-членам.

Значит ли это, что this_ptr должен быть объявлен как указатель на изменчивые данные? Должен ли "флаг" быть волатильным? И если да, должен ли я сделать все функции-члены, изменяющие флаг, изменчивыми?

Меня не интересует, как ведет себя конкретная ОС или компилятор, я ищу универсальное, полностью переносимое решение.

РЕДАКТИРОВАТЬ: Этот вопрос не имеет ничего общего с безопасностью потоков!

В реальном коде будут семафоры и т. Д.

Чтобы уточнить, я хочу избежать ошибок, вызванных неосведомленностью компилятора о том, что функция обратного вызова может вызываться из источников вне самой программы, и поэтому делать неверные выводы о том, использовались ли определенные переменные или нет. Я знаю, как сделать это в C, как показано в первом примере, но не в C ++.

Ответы [ 4 ]

2 голосов
/ 25 февраля 2011

Ну, это редактирование имеет все значение мира.Семафоры вводят барьеры памяти.Те делают volatile избыточным.Компилятор всегда перезагрузит int flag после любой операции с семафором.

Фред Ларсон уже предсказал это.volatile недостаточно при отсутствии замков и избыточно при наличии замков.Это делает его бесполезным для поточно-ориентированного программирования.

0 голосов
/ 25 февраля 2011

Некоторая реализация резервного механизма приведена здесь как для Windows, так и для Linux.Попробуйте этот пример :

typeReadFileCallback varCallback;

Я смог реализовать с помощью этого.

0 голосов
/ 25 февраля 2011

Прочтите эту статью Андрея Александреску в Dr. Dobbs, это может быть актуально:

volatile - лучший друг многопоточного программиста

От вступления к статье:

Волатильное ключевое слово было разработано для предотвратить оптимизацию компилятора, которая может сделать код неверным в наличие определенного асинхронного События. Например, если вы объявите Примитивная переменная как изменчивая, компилятору не разрешено его кэшировать в реестре - общая оптимизация это было бы катастрофическим, если бы это переменные были разделены между несколькими потоки. Таким образом, общее правило, если у вас есть переменные примитивного типа которые должны быть разделены между несколькими темы, объявите эти переменные неустойчивый. Но вы можете сделать намного больше с этим ключевым словом: вы можете используйте его, чтобы поймать код, который не Поток безопасно, и вы можете сделать это на время компиляции. Эта статья показывает, как сделано; решение включает в себя простой умный указатель, который также делает легко сериализовать критические секции кода.

0 голосов
/ 24 февраля 2011

Судя по сигнатуре указателя функции, я полагаю, вы используете реализацию потоков posix для потоков. Я предполагаю, что вы хотите знать, как начать поток, используя этот API. Сначала рассмотрите возможность использования boost thread . Если не вариант, я обычно использую что-то вроде следующего, чтобы получить некоторую удобную читабельность Java.

class Runnable {
    public:
        virtual void run() = 0;
};

class Thread : public Runnable {
    public:
        Thread();
        Thread(Runnable *r);

        void start();
        void join();        

        pthread_t getPthread() const;

    private:

        static void *start_routine(void *object);

        Runnable *runner;
        pthread_t thread;
};

А потом что-то вроде этого в функции start_routine:

void* Thread::start_routine(void *object) {
    Runnable *o = (Runnable *)object;

    o->run();

    pthread_exit(NULL);
    return NULL;
}

Теперь доступ к полям классов, расширяющих класс Runnable или Thread, не обязательно должен быть изменяемым, поскольку они thread-local .

Тем не менее, совместное использование данных между потоками является более сложным, чем использование изменчивого элемента данных, к сожалению, если это то, что вы просили ...

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...