2016-10-27 9 views
8

Я видел разные темы в разделе «pthread vs std :: thread» и «QThread vs pthread», но ни один из них не был «std :: thread vs QThread».QThread vs std :: thread

Мне нужно запрограммировать программное обеспечение для управления 3D-принтером и использовать потоки. Будет поток, который будет постоянно проверять безопасность, другой - выполнить процесс печати, некоторые для управления каждым аппаратным компонентом (перемещение, струя, ...) отдельно и т. Д. Программа разработана для Windows с C++ 11/Qt.

Сначала я хотел использовать QThread, но мне кажется, что QThread не позволяет сделать так много вещей, как станд :: нить, например, при чтении «C++ Параллелизм в действии» Энтони Уильямс, Я видел, что можно было спросить std :: thread выполнить функцию из другого потока, выполнив что-то вроде std::thread t1(&Class::function, this, ...);, что не представляется возможным с QThread.

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

Какой из них вы бы выбрали и почему?

+0

Хорошая вещь о 'станд :: thread' это уменьшает зависимость, если вы уже используете Qt , – NathanOliver

+1

Я бы не хотел привязывать свой * код печати * к определенной «Библиотеке GUI», если бы меня не заставили. Я не вижу недостатков в использовании 'std :: thread' в максимально возможной степени. – Galik

+0

В моей работе мы разрабатываем наш графический интерфейс с Qt, поэтому он уверен, что он будет привязан к Qt: GUI находится в Qt, а код программы принтера также использует Qt. – ElevenJune

ответ

9

QThread не просто поток, но и менеджер потоков. Если вы хотите, чтобы ваш поток играл Qt, то QThread - это путь. Qt управляется событиями, как и большинство современных программ. Это немного сложнее и гибче, чем «заставить поток запускать функцию».

В Qt вы обычно создаете рабочего вместе с QThread, переместите работника в этот поток, затем каждая функция, вызванная системой событий для этого рабочего объекта, будет выполнена в потоке, с которым рабочий объект имеет сродство к ,

Таким образом, вы можете инкапсулировать функциональность в другом объекте рабочего, скажем, SafetyCheckerPrinter, ServoDriver, JetDriver и так далее, создать экземпляр каждого, переместить его в выделенную нить и вы установлены. Вы по-прежнему можете вызывать функции, которые будут «блокировать», а не использовать мелкозернистые события, и использовать атомику или мьютексы для межинтерфейсной синхронизации. В этом нет ничего плохого, если вы не блокируете поток main/gui.

Возможно, вы не захотите выполнять управляемый код вашего кода, так как в многопоточном сценарии, который будет включать в себя очереди в виде очередей, которые незначительно медленнее прямых или даже виртуальной отправки. Настолько, что если вы сделаете многопоточность слишком тонкой, вы, вероятно, на самом деле испытаете огромный успех.

Это говорит о том, что использование не-gui-материалов Qt имеет свои достоинства, что позволит вам сделать более чистую и гибкую конструкцию намного проще, и вы все равно получите преимущества многопоточности, если будете правильно реализовывать вещи. Вы все еще можете использовать подход, управляемый событиями, чтобы управлять всем этим, это будет значительно проще, чем использование только std::thread, что является гораздо более низким уровнем конструкции. Вы можете использовать управляемый событиями подход к настройке, настройке, мониторингу и управлению дизайном, в то время как критические части могут выполняться в блокирующих функциях во вспомогательных потоках для достижения мелкозернистого управления с наименьшими возможными издержками синхронизации.

Чтобы уточнить - ответ не фокусируется на выполнении задачи асинхронной работы, поскольку два других ответа уже выполняются, и поскольку, как я упоминал в комментариях, задачи асинхронного программирования не предназначены для приложений управления. Они подходят для выполнения небольших задач, которые по-прежнему занимают больше времени, чем вы хотели бы заблокировать основной поток. Как рекомендуемое руководство, все, что занимает более 25 мс, предпочтительнее выполнять async. В то время как печать - это что-то, что может занять минуты или даже часы, и подразумевает непрерывное управление функциями управления параллельно и с использованием синхронизации. Задачи Async не дают вам гарантии производительности, времени ожидания и заказа для приложений управления.

+0

Спасибо за ваш ответ.То, что вы говорите об инкапсулировании рабочего и перемещении их в QThread, - это то, что я уже сделал: например, с помощью ServoDriver мне просто нужно вызвать ServoDriver :: moveTo (x, y), и движение выполняется в другом потоке и делает не блокировать остальную часть программы. Теперь проблема заключается в следующем: я хочу переместить, а затем распечатать что-нибудь, как мне сделать, чтобы дождаться, когда движение будет выполнено до печати? – ElevenJune

+0

Похоже, вам нужно реализовать тривиальную очередь команд. Затем вы вставляете в нее команды. Рабочий выполняет одну команду, после ее завершения она выполняет следующую процедуру, пока не будет обработана вся очередь. Каждая команда может быть представлена ​​целым числом, а затем ее параметрами, «QDataStream» является хорошим кандидатом для реализации этого. – dtech

+0

Я тоже пробовал, и это сработало, но у меня не могло быть обоих вариантов поведения в одном приложении. С тем, что я сделал сейчас, я могу одновременно разделять каждый компонент. Это очень полезно для графического интерфейса калибровки: вы говорите «двигайтесь туда», он перемещается, графический интерфейс не блокируется, отлично. Для этого я вызываю ServoDriver :: moveTo (x, y), которые отправляют сигнал классу, представляющему элемент, который я хочу переместить (для перемещения есть 4 элемента diffrents). Класс имеет обработчик сигнала на другом потоке и выполняет движение в этом потоке. С этой реализацией я не могу делать то, что вы говорите – ElevenJune

6

QThread хорошо, если вы хотите интегрировать нить в систему Qt (например, имеющие испускать сигналы или подключить определенные слоты)

Хотя расположение QThread все еще сделано, так что работает с «старой» с ++ , Вы должны создать класс и все эти накладные расходы (код и ввод текста) только для работы в потоке.

Если вы просто хотите начать тему, я думаю, что C++ 11 std::thread менее подробный/код, который вы должны написать. Вы можете просто использовать указатель лямбда или функции и можете указать столько аргументов, сколько хотите. Поэтому для простой потоковой обработки я бы предложил потоки C++ 11 поверх QThreads.

Конечно, это может быть вопрос мнения, какой из них вы предпочитаете.

Хотя Qt имеет несколько различных объектов нити высокого уровня, на C++ нет. Вы можете захотеть изучить их, если вам нужны некоторые из них, а не основной поток или, может быть, вам совсем не нужен базовый поток, но они подходят вам лучше.

Как QThreadPool или просто QTimer, если вам нужно подождать вещей. Here - это некоторое ознакомление с альтернативами в Qt для голой нити.

QConcurrent также подходит ближе к C++ 11 future и async, а также имеет дополнительные пулы потоков, в которых он может работать.

+4

Я думаю, что 'std :: thread', берущий functor/lambda, является ужасным интерфейсом, поскольку он поощряет создание эфемерных потоков с целью запуска коротких фрагментов кода. Даже ОП упал на эту ловушку! API 'QThread' сообщает вам, что потоки являются ** определенно не легкими вещами **. На практике 'std :: thread' не имеет больше накладных расходов, чем' QThread', потому что реальные накладные расходы - это намного большая стоимость создания потока операционной системой. Независимо от того, какой код вы помещаете в класс помощника, который обертывает его, он действительно несуществен. –

+3

В большинстве случаев вы должны использовать 'std :: async' или' QtConcurrent :: run', * not * 'QThread' или' std :: thread'. 'QThread' в основном полезен для перемещения' QObject' в. –

+0

@KubaOber с «накладными расходами» Я имел в виду количество ввода/кода, которое вам нужно сделать, а не производительность. Но я должен это уточнить. Но я согласен. В большинстве случаев вы должны предпочесть API более высокого уровня – Hayt

6

Основная проблема с std::thread и QThread заключается в том, что он делает то, что он говорит на олове: создает для вас один поток, который, вероятно, будет делать только одно. Выполнение функции «одновременно» с использованием std::thread очень расточительно: потоки - это дорогостоящие ресурсы, поэтому создание одного только для выполнения некоторых функций обычно является излишним.

В то время как std::thread t1(&Class::function, this, ...) выглядит красиво, и все это, как правило, является преждевременным пессимизмом и предполагает его как универсальный способ «делать вещи одновременно», это ИМХО неправильно. Вы можете сделать лучше.

  1. Если вы хотите запустить функцию/функтор/метод одновременно в рабочем потоке, используйте QtConcurrent::run или std::async.

    QtConcurrent::run использует пул потоков по умолчанию по умолчанию, вы также можете передать свой собственный экземпляр QThreadPool. Типичным случаем было бы использовать пул потоков по умолчанию для задач, связанных с процессором, например. вычислений, преобразований изображений, рендеринга и т. д. и использовать выделенный более большой пул потоков ввода-вывода для выполнения операций, которые блокируются из-за ограничений API, которые вы вынуждены использовать (например, многие библиотеки баз данных предлагают только блокирующие API, потому что их конструкции принципиально нарушены).Пример:

    // interface 
    QThreadPool * ioPool(); 
    
    // implementation 
    Q_GLOBAL_STATIC(QThreadPool, ioPool_impl); 
    QThreadPool * ioPool() { return ioPool_impl; } 
    
  2. Если вы хотите иметь QObject жить в другом потоке (возможно, совместно habitating с другими объектами), используйте QThread затем переместите объект в эту тему, используя moveToThread.

    Это идиома, чтобы излучать сигнал от рабочего потока, чтобы поточно-безопасно передавать данные в основной поток. Например. Предположим, вы хотите иметь отзывчивую графический интерфейс и хотите загрузить изображения с диска в рабочем потоке:

    class MyWidget : public QWidget { 
        QLabel m_label; 
        ... 
        Q_SIGNAL void setImage(const QImage &); 
    public: 
        MyWidget() { 
        ... 
        connect(MyWidget, &MyWidget::setImage, this, [this](const QImage & image){ 
        m_label.setPixmap(QPixmap::fromImage(image)); 
        }); 
        QtConcurrent::run(ioPool(), [this]{ setImage({"/path/to/image.png"}); }); 
        } 
    }; 
    
+0

Я сомневаюсь, что QtConcurrent :: run будет хорошим кандидатом на что-то вроде вождения принтера. – dtech

+0

@ddriver Если вы используете пул ввода-вывода, это прекрасно, если принтер можно использовать из другого потока. –

+0

Очень ясный ответ, я заглянул в QtConcurrent и, похоже, отлично. Спасибо ! – ElevenJune

Смежные вопросы