2015-07-29 3 views
0

Я относительно новичок в Qt и pthreads, но я пытаюсь использовать pthread для работы на фоне базового тестового приложения, которое я создаю. Я знаю о собственной структуре потоковой обработки Qt Frameworks, но есть много жалоб, связанных с этим, поэтому я хотел бы использовать pthread, если это возможно. Код нижеИспользование потоков POSIX в приложении Qt Widget

#include "drawwindow.h" 
#include "ui_drawwindow.h" 
#include <pthread.h> 
#include <stdlib.h> 
#include <stdio.h> 
#include "QThread" 

pthread_t th1; 

DrawWindow::DrawWindow(QWidget *parent) : 
    QMainWindow(parent), 
    ui(new Ui::DrawWindow) 
{ 
    ui->setupUi(this); 
} 

DrawWindow::~DrawWindow() 
{ 
    delete ui; 
} 

void DrawWindow::on_pushButton_clicked() 
{ 
    pthread_create(&th1, NULL, &DrawWindow::alter_text, NULL); 
} 

void DrawWindow::alter_text() 
{ 
    while(1) 
    { 
     ui->pushButton->setText("1"); 
     QThread::sleep(1); 
     ui->pushButton->setText("one"); 
     QThread::sleep(1); 

    } 
} 

С заголовком

#ifndef DRAWWINDOW_H 
#define DRAWWINDOW_H 

#include <QMainWindow> 

namespace Ui { 
class DrawWindow; 
} 

class DrawWindow : public QMainWindow 
{ 
    Q_OBJECT 

public: 
    explicit DrawWindow(QWidget *parent = 0); 
    ~DrawWindow(); 
    void alter_text(); 


private slots: 
    void on_pushButton_clicked(); 

private: 
    Ui::DrawWindow *ui; 

}; 

#endif // DRAWWINDOW_H 

И я получаю ошибку

error: cannot convert 'void (DrawWindow::*)()' to 'void* (*)(void*)' for argument '3' to 'int pthread_create(pthread_t*, const pthread_attr_t*, void* (*)(void*), void*)' 
pthread_create(&th1, NULL, &DrawWindow::alter_text, NULL); 
                 ^

Кто-нибудь знает, что это не так?

+2

* «QThread ... но есть много жалоб, окружающих его» *. - Что именно вы имеете в виду? QThread работает очень хорошо, и если вы используете сигналы и слоты, вам лучше использовать QThread, чем pthread. – TheDarkKnight

+0

Работа с потоками в родах l сложно, поэтому вы видите больше людей, жалующихся, но QThread прост и прочен. – Marco

+0

** TL; DR ** Просто не делай этого. Интерфейсы 'QThread',' std :: thread' и 'pthread' очень похожи, за исключением того, что' pthread' - ужасная C-мерка, которая не имеет места в коде C++. 'QThread' и' std :: thread', так же как API 'pthread', за исключением того, что вы можете использовать C++, чтобы сделать жизнь проще и стрелять в вашу ногу гораздо сложнее. ** Плохая идея прислушаться к «жалобам» без их полного понимания **! –

ответ

0

void DrawWindow::alter_text() - void* DrawWindow::alter_text(void*) и возвращает pthread_exit(NULL);.

0

TL; DR: Способ использования pthreads - это, как правило, обескураженный способ использования QThread. Просто потому, что вы используете другой api, это не значит, что то, что вы делаете, в порядке.

Нет проблем ни с QThread, ни с std::thread. Забудьте о pthreads: они не переносимы, их API - C и, таким образом, отвратителен с точки зрения программиста на C++, и вы будете делать свою жизнь несчастной без причины, придерживаясь pthreads.

Ваша настоящая проблема в том, что вы не поняли проблемы с QThread. Есть два:

  1. Ни QThread, ни std::thread являются разрушаемые во все времена. Хороший дизайн C++ требует, чтобы классы были разрушаемыми в любое время.

    Вы не можете уничтожить бег QThread или std::thread. Вы должны сначала убедиться, что он остановлен, позвонив соответственно QThread::wait() или std::thread::join(). Это было бы не так уж важно, чтобы их деструкторы это сделали, а также остановили цикл событий в случае QThread.

  2. Слишком часто люди используют QThread переопределения методы run, или они используют std::thread, запустив функтор на нем. Это, конечно, именно то, как вы используете pthreads: вы запускаете какую-то функцию в выделенном потоке. То, как вы используете pthreads, так же плохо, как обескураженный способ использования QThread!

    Существует много способов делать многопоточность в Qt, и вы должны понимать плюсы и минусы каждого из них.

Таким образом, как вы выполняете потоки в C++/Qt?

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

  1. Использовать QThread без подкласса.По умолчанию реализация run() просто объединяет цикл событий, который позволяет объектам запускать свои таймеры и получать события и очереди вызовов слотов. Запустите нить, затем переместите несколько QObject экземпляров к нему. Экземпляры будут запускаться в этом потоке и могут выполнять любую работу, которая им нужна, вдали от основного потока. Конечно, все, что объекты делают, должно быть коротким, код запуска до завершения, который не блокирует поток.

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

  2. QtConcurrent::run. Это похоже на GCD от Apple. Существует глобальный QThreadPool. Когда вы запускаете функтор, один поток из пула будет разбужен и будет выполнять функтор. Количество потоков в пуле ограничено количеством ядер, доступных в системе. Использование большего количества потоков, чем это приведет к снижению производительности.

    Функторы, которые вы передаете run, будут выполнять автономные задачи, которые в противном случае блокировали бы графический интерфейс, приводящий к проблемам удобства использования. Например, используйте его для загрузки или сохранения изображения, выполнения куска вычислений и т. Д.

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

    class Loader : public QObject { 
        Q_OBJECT 
    public: 
        Q_SIGNAL void hasImage(const QImage &, const QString & path); 
        explicit Loader(const QStringList & imagePaths, QObject * parent = 0) : 
        QObject(parent) { 
        QtConcurrent::map(imagePaths, [this](const QString & path){ 
         QImage image; 
         image.load(path); 
         emit hasImage(image, path); 
        }); 
        } 
    }; 
    

    Если вы хотите запустить недолгий QObject в потоке из пула потоков, функтор может закрутить цикл событий следующим образом:

    auto foo = QSharedPointer<Object>(new Object); // Object inherits QObject 
    foo->moveToThread(0); // prepares the object to be moved to any thread 
    QtConcurrent::run([foo]{ 
        foo->moveToThread(QThread::currentThread()); 
        QEventLoop loop; 
        QObject::connect(foo, &Object::finished, &loop, &QEventLoop::quit); 
        loop.exec(); 
    }); 
    

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

  3. Используйте специальный поток для запуска функтора или метода. Разница между QThread и std::thread в основном такова, что std::thread позволяет использовать функторы, тогда как QThread требует подкласса. API pthread похож на std::thread, за исключением того, что он является C и ужасно опасен по сравнению с API C++.

    // QThread 
    int main() { 
        class MyThread : public QThread { 
        void run() { qDebug() << "Hello from other thread"; } 
        } thread; 
        thread.start(); 
        thread.wait(); 
        return 0; 
    } 
    
    // std::thread 
    int main() { 
        // C++98 
        class Functor { 
        void operator()() { qDebug() << "Hello from another thread"; } 
        } functor; 
        std::thread thread98(functor); 
        thread98.join(); 
        // C++11 
        std::thread thread11([]{ qDebug() << "Hello from another thread"; }); 
        thread11.join(); 
        return 0; 
    } 
    
    // pthread 
    extern "C" void* functor(void*) { qDebug() << "Hello from another thread"; } 
    
    int main() 
    { 
        pthread_t thread; 
        pthread_create(&thread, NULL, &functor, NULL); 
        void * result; 
        pthread_join(thread, &result); 
        return 0; 
    } 
    

    Для чего это полезно? Иногда у вас нет выбора, кроме как использовать блокирующий API. Большинство драйверов баз данных, например, имеют только блокирующие API. Они не выдают никакого способа, чтобы ваш код получал уведомление, когда запрос был завершен. Единственный способ использовать их - запустить блокирующую функцию/метод запроса, которая не возвращается до тех пор, пока запрос не будет выполнен. Предположим теперь, что вы используете базу данных в приложении графического интерфейса, которое вы хотите сохранить отзывчивым. Если вы запускаете запросы из основного потока, графический интерфейс будет блокироваться при каждом запуске запроса базы данных. Учитывая длительные запросы, перегруженная сеть, dev-сервер с откидным кабелем, который заставляет TCP выполнять наравне с sneakernet ... вы столкнулись с огромными проблемами юзабилити.

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

    Даже в этом случае может оказаться полезным использовать в потоке QObject и прокрутить цикл событий, так как это позволит вам легко ставить в очередь запросы базы данных без необходимости писать собственную потокобезопасную очередь. Цикл событий Qt уже реализует красивую потокобезопасную очередь событий, чтобы вы могли ее использовать. Например, с учетом того, что модуль SQL Qt может использоваться только из одного потока - таким образом, вы не можете подготовить QSQLQuery в основной теме :(

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

    class DBWorker : public QObject { 
        Q_OBJECT 
        QScopedPointer<QSqlDatabase> m_db; 
        QScopedPointer<QSqlQuery> m_qBooks, m_query2; 
        Q_SLOT void init() { 
        m_db.reset(new QSqlDatabase(QSqlDatabase::addDatabase("QSQLITE"))); 
        m_db->setDatabaseName(":memory:"); 
        if (!m_db->open()) { emit openFailed(); return; } 
        m_qBooks.reset(new QSqlQuery(*m_db)); 
        m_qBooks->prepare("SELECT * FROM Books"); 
        m_qCars.reset(new QSqlQuery(*m_db)); 
        m_qCars->prepare("SELECT * FROM Cars"); 
        } 
        QList<QVariantList> read(QSqlQuery * query) { 
        QList<QVariantList> result; 
        result.reserve(query->size());  
        while (query->next()) { 
         QVariantList row; 
         auto record = query->record(); 
         row.reserve(record.count()); 
         for (int i = 0; i < recourd.count(); ++i) 
         row << query->value(i); 
         result << row; 
        } 
        return result; 
        } 
    public: 
        typedef QList<QVariantList> Books, Cars; 
        DBWorker(QObject * parent = 0) : QObject(parent) { 
        QObject src; 
        connect(&src, &QObject::destroyed, this, &DBWorker::init, Qt::QueuedConnection); 
        m_db.moveToThread(0 
        } 
        Q_SIGNAL void openFailed(); 
        Q_SIGNAL void gotBooks(const DBWorker::Books &); 
        Q_SIGNAL void gotCars(const DBWorker::Cars &); 
        Q_SLOT void getBooks() { 
        Q_ASSERT(QThread::currentThread() == thread()); 
        m_qBooks->exec(); 
        emit gotBooks(read(m_qBooks)); 
        } 
        Q_SLOT void getCars() { 
        Q_ASSERT(QThread::currentThread() == thread()); 
        m_qCars->exec(); 
        emit gotCars(read(m_qCars)); 
        } 
    }; 
    Q_REGISTER_METATYPE(DBWorker::Books); 
    Q_REGISTER_METATYPE(DBWorker::Cars); 
    
    // True C++ RAII thread. 
    Thread : public QThread { using QThread::run; public: ~Thread() { quit(); wait(); } }; 
    int main(int argc, char ** argv) { 
        QCoreApplication app(argc, argv); 
        Thread thread; 
        DBWorker worker; 
        worker.moveToThread(&thread); 
        QObject::connect(&worker, &DBWorker::gotCars, [](const DBWorker::Cars & cars){ 
        qDebug() << "got cars:" << cars; 
        qApp->quit(); 
        }); 
        thread.start(); 
        ... 
        QMetaObject::invokeMethod(&worker, "getBooks"); // safely invoke `getBooks` 
        return app.exec(); 
    } 
    
Смежные вопросы