Successfully reported this slideshow.
Your SlideShare is downloading. ×

C++ STL & Qt. Занятие 08.

Ad
Ad
Ad
Ad
Ad
Ad
Ad
Ad
Ad
Ad
Ad
Loading in …3
×

Check these out next

1 of 27 Ad

More Related Content

Slideshows for you (20)

Viewers also liked (20)

Advertisement

Similar to C++ STL & Qt. Занятие 08. (20)

More from Igor Shkulipa (20)

Advertisement

Recently uploaded (20)

C++ STL & Qt. Занятие 08.

  1. 1. Темы лекции: Многопоточность в Qt. Практическое задание: Многопоточность в Qt. Тренер: Игорь Шкулипа, к.т.н. С++ Библиотеки STL и Qt. Занятие 8
  2. 2. http://www.slideshare.net/IgorShkulipa 2 Object Pool Применение паттерна Object Pool может значительно повысить производительность системы; его использование наиболее эффективно в ситуациях, когда создание экземпляров некоторого класса требует больших затрат, объекты в системе создаются часто, но число создаваемых объектов в единицу времени ограничено. Пулы объектов (известны также как пулы ресурсов) используются для управления кэшированием объектов. Клиент, имеющий доступ к пулу объектов может избежать создания новых объектов, просто запрашивая в пуле уже созданный экземпляр. Пул объектов может быть растущим, когда при отсутствии свободных создаются новые объекты или c ограничением количества создаваемых объектов.
  3. 3. http://www.slideshare.net/IgorShkulipa 3 Реализация Object Pool на основе Singleton. Классы объектов class IObject { protected: string _strText; public: virtual void Print() { cout<<"The Object is: "<<_strText.c_str()<<"n"; } }; class Object1: public IObject { public: Object1(){ _strText="Object 1"; } }; class Object2: public IObject { public: Object2(){ _strText="Object 2"; } }; class Object3: public IObject { public: Object3(){ _strText="Object 3"; } }; class Object4: public IObject { public: Object4(){ _strText="Object 4"; } };
  4. 4. http://www.slideshare.net/IgorShkulipa 4 Object Pool template<unsigned poolSize> class ObjectPool{ public: static ObjectPool* GetInstance() { if (!_instance) _instance=new ObjectPool(); return _instance; } IObject* GetObject() { for (unsigned i=0;i<poolSize;i++) { if (!_busyObjects[i]){ _busyObjects[i]=true; return _objectPool[i]; } } return NULL; } void ReleaseObject(IObject* object){ for (unsigned i=0;i<poolSize;i++) { if (_objectPool[i]==object){ _busyObjects[i]=false; } } } ...
  5. 5. http://www.slideshare.net/IgorShkulipa 5 Object Pool ... private: ObjectPool(){ for (unsigned i=0;i<poolSize;i++) { unsigned iObjNumber=rand()%4; switch (iObjNumber) { case 0: _objectPool[i]=new Object1(); break; case 1: _objectPool[i]=new Object2(); break; case 2: _objectPool[i]=new Object3(); break; case 3: _objectPool[i]=new Object4(); break; } _busyObjects[i]=false; } } private: IObject* _objectPool[poolSize]; bool _busyObjects[poolSize]; static ObjectPool* _instance; };
  6. 6. http://www.slideshare.net/IgorShkulipa 6 Использование Object Pool template<unsigned poolSize> ObjectPool<poolSize>* ObjectPool<poolSize>::_instance=NULL; int main() { ObjectPool<5>* op=ObjectPool<5>::GetInstance(); IObject* object1=op->GetObject(); if (object1) object1->Print(); else cout<<"The Object is: NULLn"; IObject* object2=op->GetObject(); if (object2) object2->Print(); else cout<<"The Object is: NULLn"; IObject* object3=op->GetObject(); if (object3) object3->Print(); else cout<<"The Object is: NULLn"; IObject* object4=op->GetObject(); if (object4) object4->Print(); else cout<<"The Object is: NULLn"; IObject* object5=op->GetObject(); if (object5) object5->Print(); else cout<<"The Object is: NULLn"; IObject* object6=op->GetObject(); if (object6) object6->Print(); else cout<<"The Object is: NULLn"; IObject* object7=op->GetObject(); if (object7) object7->Print(); else cout<<"The Object is: NULLn"; op->ReleaseObject(object2); IObject* object8=op->GetObject(); if (object8) object8->Print(); else cout<<"The Object is: NULLn"; }
  7. 7. http://www.slideshare.net/IgorShkulipa 7 Результат The Object is: Object 2 The Object is: Object 4 The Object is: Object 3 The Object is: Object 1 The Object is: Object 2 The Object is: NULL The Object is: NULL The Object is: Object 4
  8. 8. http://www.slideshare.net/IgorShkulipa 8 Преимущества и недостатки ◦ Пул объектов отслеживает объекты, которые он создает. ◦ Паттерн Object Pool может использоваться для инкапсуляции логики создания объектов. Однако он не управляет ими после их создания. ◦ Достоинством этого паттерна является быстрое создание объектов, однако это реализовано за счет использования больших ресурсов памяти.
  9. 9. http://www.slideshare.net/IgorShkulipa 9 Процессы и потоки Процессы представляют собой программы, независимые друг от друга и загруженные для исполнения. Каждый процесс должен создавать хотя бы один поток, называемый основным. Основной поток процесса создается в момент запуска программы. Однако сам процесс может создавать несколько потоков одновременно. Многопоточность позволяет разделять задачи и независимо работать над каждой из них для того, чтобы максимально эффективно задействовать процессор. Написание многопоточных приложений требует больше времени и усложняет процесс отладки, поэтому многопоточность нужно применять тогда, когда это действительно необходимо. Многопоточность удобно использовать для того, чтобы блокировка или зависание одного из методов не стали причиной нарушения функционирования основной программы.
  10. 10. http://www.slideshare.net/IgorShkulipa 10 QProcess Процесс — это экземпляр программы, загруженной в память компьютера для выполнения. Создание процесса может оказаться полезным для использования функциональных возможностей программ, не имеющих графического интерфейса и работающих с командной строкой. Другое полезное свойство — довольно простой запуск других программ из текущей программы. Процессы можно создавать с помощью класса QProcess, который определен в заголовочном файле QProcess. Благодаря тому, что этот класс унаследован от класса QIODevice, объекты этого класса в состоянии считывать информацию, выводимую запущенными процессами, и даже подтверждать их запросы на ввод информации. Этот класс содержит методы для манипулирования системными переменными процесса. Работа с объектами класса QProcess производится в асинхронном режиме, что позволяет сохранять работоспособность графического интерфейса программы в моменты, когда запущенные процессы находятся в работе.
  11. 11. http://www.slideshare.net/IgorShkulipa 11 QThread Для использования многопоточности можно унаследовать класс от QThread и перезаписать метод run(), в который должен быть помещен код для исполнения в потоке. Чтобы запустить поток, нужно вызвать метод start(). class CustomThread : public QThread { public: CustomThread(); void setMessage(const QString &message); void run() {/*Действия потока*/}; void stop(); private: QString messageStr; volatile bool stopped; }; //... int main(int argc, char** argv) { CustomThread thread; thread.start(); }
  12. 12. http://www.slideshare.net/IgorShkulipa 12 Приоритет потоков У каждого потока есть приоритет, указывающий процессору, как должно протекать выполнение потока по отношению к другим потокам. Приоритеты разделяются по группам: • в первую входят четыре наиболее часто применяемых приоритета. Их значимость распределяется по возрастанию — IdlePriority, LowestPriority, LowPriority, NormaiPriority. Они подходят для решения задач, которым процессор требуется только время от времени, например, для фоновой печати или для каких-нибудь несрочных действий; • во вторую группу входят два приоритета — HighPriority, HighestPriority. Пользуйтесь такими приоритетами с большой осторожностью. Обычно эти потоки большую часть времени ожидают какие-либо события; • в третью входят два приоритета — TimeCriticalPriority, InheritPriority. Потоки с этими приоритетами нужно создавать в случаях крайней необходимости. Эти приоритеты нужны для программ, напрямую общающихся с аппаратурой или выполняющих операции, которые ни в коем случае не должны прерваться. Для того чтобы запустить поток с нужным приоритетом, необходимо передать одно из приведенных выше значений в метод start(). Например: CustomThread thread; thread.start(QThread::IdlePriority);
  13. 13. http://www.slideshare.net/IgorShkulipa 13 Синхронизация Основные сложности возникают тогда, когда потокам нужно совместно использовать одни и те же данные. Так как несколько потоков могут одновременно обращаться и записывать данные в одну область, то это может привести к нежелательным последствиям. Синхронизация позволяет задавать критические секции (critical sections), к которым в определенный момент имеет доступ только один из потоков. Это гарантирует то, что данные ресурса, контролируемые критической секцией, будут невидимы другими потоками и они не изменят их. И только после того, как поток выполнит всю необходимую работу, он освобождает ресурс, и, затем, доступ к этому ресурсу может получить любой другой поток. Например, если один поток записывает информацию в файл, то все другие не смогут использовать этот файл до тех пор, пока поток не освободит его.
  14. 14. http://www.slideshare.net/IgorShkulipa 14 Объекты синхронизации Mutex Мьютексы (mutex) обеспечивают взаимоисключающий доступ к ресурсам, гарантирующий то, что критическая секция будет обрабатываться только одним потоком. Поток, владеющий мьютексом, обладает эксклюзивным правом на использование ресурса, защищенного мьютексом, и другой поток не может завладеть уже занятым мьютексом. Механизм мьютексов реализован классом QMutex. Метод lock() класса QMutex производит блокировку ресурса. Для обратной операции существует метод unlock(), который открывает закрытый ресурс для других потоков. Класс QMutex также содержит метод tryLock(). Этот метод можно использовать для того, чтобы проверить, заблокирован ресурс или нет. Этот метод не приостанавливает исполнение потока и возвращается немедленно, со значением false, если ресурс уже захвачен другим потоком, и не ожидает его освобождения. В случае успешного захвата ресурса этот метод вернет true. В сложных функциях, особенно при использовании исключений C++, легко можно ошибиться при выполнении последовательностей операций по запиранию/отпиранию мьютексов. Поэтому, в состав Qt включен класс QMutexLocker, который значительно упрощает работу с мьютексами. Конструктор класса QMutexLocker принимает объект QMutex в виде аргумента и запирает его. Деструктор класса QMutexLocker - отпирает мьютекс.
  15. 15. http://www.slideshare.net/IgorShkulipa 15 Пример «Потокобезопасный стек» template <class Type> class ThreadSafeStack { public: void push(const Type& val) { mutex.lock(); stack.push(val); mutex.unlock(); } Type pop() { QMutexLocker locker (&mutex); return stack.empty() ? Type() : stack.pop(); } private: QMutex mutex; QStack<Type> stack; };
  16. 16. http://www.slideshare.net/IgorShkulipa 16 QWaitCondition Библиотека Qt предоставляет класс QWaitCondition, обеспечивающий возможность координации потоков. Если поток намеревается дождаться разблокировки ресурса, то он вызывает метод QWaitCondition::wait() и, тем самым, входит в режим ожидания. Выводится он из этого режима в том случае, если поток, который заблокировал ресурс, вызовет метод QWaitCondition::wakeOne() или QWaitCondition::wakeAll(). Разница этих двух методов в том, что первый выводит из состояния ожидания только один поток, а второй — все сразу. Также для потока можно установить время, в течение которого он может ожидать разблокировки данных. Для этого нужно передать в метод wait() целочисленное значение, обозначающее временной интервал в миллисекундах.
  17. 17. http://www.slideshare.net/IgorShkulipa 17 Семафоры Семафоры являются обобщением мьютексов. Как и мьютексы, они служат для защиты критических секций, чтобы доступ к ним одновременно могло иметь определенное число потоков. Все другие потоки обязаны ждать. Предположим, что программа поддерживает пять ресурсов одного и того же типа, одновременный доступ к которым может быть предоставлен только пяти потокам. Как только все пять ресурсов будут заблокированы, следующий поток, запрашивающий ресурс данного типа, будет приостановлен до освобождения одного из них. Принцип действия семафоров очень прост. Они начинают действовать с установленного значения счетчика. Каждый раз, когда поток получает право на владение ресурсом, значение этого счетчика уменьшается на единицу. И наоборот, когда поток уступает право владения этим ресурсом, счетчик увеличивается на единицу. При значении счетчика равном нулю семафор становится недоступным. Механизм семафоров реализует класс QSemaphore. Счетчик устанавливается в конструкторе при создании объекта этого класса.
  18. 18. http://www.slideshare.net/IgorShkulipa 18 Взаимная блокировка При работе с многопоточностью, возможна такая ситуация, когда поток заблокировал ресурс А, а после работы над ним собирается работать с ресурсом В. Другой же поток заблокировал ресурс В и по окончании намеревается работать с ресурсом А. И вот один из потоков, закончив работу, обнаружил, что нужный ему ресурс заблокирован другим потоком. Он переходит в режим ожидания, надеясь дождаться разблокировки ресурса, но то же самое делает и другой поток. В итоге — оба ждут друг друга. Если ни один из этих потоков не освободит занятый им ресурс, то оба "зависнут" и не смогут продолжать свою работу дальше. Это явление получило название взаимной блокировки (deadlock). Существует множество решений такой проблемы. Например, можно так организовать работу потока, чтобы, в том случае, если поток не сможет получить доступ к необходимому ресурсу, он просто произвел бы освобождение занятых им ресурсов, а позже повторил попытку захвата необходимых ресурсов.
  19. 19. http://www.slideshare.net/IgorShkulipa 19 Обмен сообщениями между потоками Один из важнейших вопросов при многопоточном программировании — это обмен сообщениями. Каждый поток может иметь свой собственный цикл событий. Благодаря этому можно осуществлять связь между объектами. Такая связь может производиться двумя способами: при помощи соединения сигналов и слотов или обмена событиями. Класс QObject реализован так, что обладает близостью к потокам. Каждый объект, произведенный от унаследованного от QObject класса, располагает ссылкой на поток, в котором он был создан. Эту ссылку можно получить вызовом метода QObject::thread(). Потоки осведомляют свои объекты. Благодаря этому каждый объект знает, к какому потоку он принадлежит. Обработка событий производится из контекста принадлежности объекта к потоку, то есть обработка его событий будет производиться в том потоке, которому объект принадлежит. Объекты можно перемещать из одного потока в другой с помощью метода QObject::moveToThread().
  20. 20. http://www.slideshare.net/IgorShkulipa 20 Отправка событий Отправка событий — это еще одна из возможностей для осуществления связи между объектами. Есть два метода для высылки событий - QCoreApplication::postEvent() и QCoreApplication::sendEvent(). Для того чтобы объект потока был в состоянии обрабатывать получаемые события, в классе потока нужно реализовать метод QObject::event(). Если поток предназначен исключительно для отправки событий, а не для их получения, то реализацию методов обработки событий и запуск цикла обработки событий можно опустить.
  21. 21. http://www.slideshare.net/IgorShkulipa 21 Сигналы и слоты Можно взять сигнал объекта одного потока и соединить его со слотом объекта другого потока. Как мы уже знаем, соединение с помощью метода connect() предоставляет дополнительный параметр, обозначающий режим обработки и равный, по умолчанию, значению Qt::AutoConnection, которое соответствует автоматическому режиму. Как только происходит высылка сигнала, Qt проверяет — происходит связь в одном и том же или разных потоках. Если это один и тот же поток, то высылка сигнала приведет к прямому вызову метода. В том случае, если это разные потоки, сигнал будет преобразован в событие и доставлен нужному объекту. Сигналы и слоты в Qt реализованы с механизмом надежности работы в потоках, а это означает, что вы можете высылать сигналы и получать, не заботясь о блокировке ресурсов.
  22. 22. http://www.slideshare.net/IgorShkulipa 22 Пример «Спам-бот». См. Занятие №8. #ifndef SPAMBOT_H #define SPAMBOT_H #include <QObject> #include <QString> #include <QTime> #include "chatmessage.h" class SpamBot: public QObject { Q_OBJECT public: SpamBot(); void stop(); void run(); public slots: void handleRun(); signals: void sendSpam(); private: volatile bool stopped; }; #endif // SPAMBOT_H
  23. 23. http://www.slideshare.net/IgorShkulipa 23 Реализация класса #include "spambot.h" SpamBot::SpamBot() { stopped=false; } void SpamBot::stop() { stopped=true; } void SpamBot::run() { while(!stopped) { QTime endTime= QTime::currentTime().addSecs(3); while( QTime::currentTime() < endTime ) { //Waiting... } emit sendSpam(); } } void SpamBot::handleRun() { this->run(); }
  24. 24. http://www.slideshare.net/IgorShkulipa 24 Дополненный презентер class ChatClientPresenter: public QObject { Q_OBJECT public: ChatClientPresenter(ChatClient* cl, IView* v); ChatClientPresenter(IView* v); void emitRunBot() { emit runBot(); } signals: void runBot(); private slots: void DisplayMessage(); void SendMessage(); void SendMessageSpam(); private: ChatClient* client; IView* view; }; void ChatClientPresenter::SendMessageSpam() { ChatMessage message("SpamBot","Spam"); client->sendToServer(message); }
  25. 25. http://www.slideshare.net/IgorShkulipa 25 Функция main #include <QtGui/QApplication> #include "mainwindow.h" #include "chatclientpresenter.h" #include "spambot.h" #include <QThread> int main(int argc, char *argv[]) { QApplication a(argc, argv); MainWindow* view=new MainWindow(); ChatClientPresenter* present=new ChatClientPresenter(view); view->show(); QThread *thread=new QThread(); SpamBot *spamBot=new SpamBot(); QObject::connect(spamBot, SIGNAL(sendSpam()), present,SLOT(SendMessageSpam()),Qt::QueuedConnection); QObject::connect(present, SIGNAL(runBot()), spamBot, SLOT(handleRun()), Qt::QueuedConnection); spamBot->moveToThread(thread); thread->start(); present->emitRunBot(); return a.exec(); }
  26. 26. http://www.slideshare.net/IgorShkulipa 26 Результат
  27. 27. http://www.slideshare.net/IgorShkulipa 27 Лабораторная работа №8. Многопоточность Создать в тетрисе дополнительный поток, который с определенной периодичностью будет обновлять рейтинг пользователей на форме (см. Лабораторную работу №5).

×