SlideShare a Scribd company logo
Темы лекции: Многопоточность в Qt.
Практическое задание: Многопоточность в Qt.
Тренер: Игорь Шкулипа, к.т.н.
С++ Библиотеки STL и Qt. Занятие 8
http://www.slideshare.net/IgorShkulipa 2
Object Pool
Применение паттерна Object Pool может значительно повысить
производительность системы; его использование наиболее
эффективно в ситуациях, когда создание экземпляров
некоторого класса требует больших затрат, объекты в системе
создаются часто, но число создаваемых объектов в единицу
времени ограничено.
Пулы объектов (известны также как пулы ресурсов)
используются для управления кэшированием объектов.
Клиент, имеющий доступ к пулу объектов может избежать
создания новых объектов, просто запрашивая в пуле уже
созданный экземпляр. Пул объектов может быть растущим,
когда при отсутствии свободных создаются новые объекты или
c ограничением количества создаваемых объектов.
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";
} };
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;
} }
}
...
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;
};
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";
}
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
http://www.slideshare.net/IgorShkulipa 8
Преимущества и недостатки
◦ Пул объектов отслеживает объекты, которые он создает.
◦ Паттерн Object Pool может использоваться для инкапсуляции
логики создания объектов. Однако он не управляет ими
после их создания.
◦ Достоинством этого паттерна является быстрое создание
объектов, однако это реализовано за счет использования
больших ресурсов памяти.
http://www.slideshare.net/IgorShkulipa 9
Процессы и потоки
Процессы представляют собой программы, независимые друг от друга и
загруженные для исполнения. Каждый процесс должен создавать хотя
бы один поток, называемый основным. Основной поток процесса
создается в момент запуска программы. Однако сам процесс может
создавать несколько потоков одновременно.
Многопоточность позволяет разделять задачи и независимо работать над
каждой из них для того, чтобы максимально эффективно
задействовать процессор. Написание многопоточных приложений
требует больше времени и усложняет процесс отладки, поэтому
многопоточность нужно применять тогда, когда это действительно
необходимо.
Многопоточность удобно использовать для того, чтобы блокировка или
зависание одного из методов не стали причиной нарушения
функционирования основной программы.
http://www.slideshare.net/IgorShkulipa 10
QProcess
Процесс — это экземпляр программы, загруженной в память компьютера
для выполнения.
Создание процесса может оказаться полезным для использования
функциональных возможностей программ, не имеющих графического
интерфейса и работающих с командной строкой. Другое полезное
свойство — довольно простой запуск других программ из текущей
программы.
Процессы можно создавать с помощью класса QProcess, который
определен в заголовочном файле QProcess. Благодаря тому, что этот
класс унаследован от класса QIODevice, объекты этого класса в
состоянии считывать информацию, выводимую запущенными
процессами, и даже подтверждать их запросы на ввод информации.
Этот класс содержит методы для манипулирования системными
переменными процесса.
Работа с объектами класса QProcess производится в асинхронном
режиме, что позволяет сохранять работоспособность графического
интерфейса программы в моменты, когда запущенные процессы
находятся в работе.
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();
}
http://www.slideshare.net/IgorShkulipa 12
Приоритет потоков
У каждого потока есть приоритет, указывающий процессору, как должно протекать
выполнение потока по отношению к другим потокам. Приоритеты разделяются
по группам:
• в первую входят четыре наиболее часто применяемых приоритета. Их
значимость распределяется по возрастанию — IdlePriority, LowestPriority,
LowPriority, NormaiPriority. Они подходят для решения задач, которым
процессор требуется только время от времени, например, для фоновой
печати или для каких-нибудь несрочных действий;
• во вторую группу входят два приоритета — HighPriority, HighestPriority.
Пользуйтесь такими приоритетами с большой осторожностью. Обычно эти
потоки большую часть времени ожидают какие-либо события;
• в третью входят два приоритета — TimeCriticalPriority, InheritPriority.
Потоки с этими приоритетами нужно создавать в случаях крайней
необходимости. Эти приоритеты нужны для программ, напрямую
общающихся с аппаратурой или выполняющих операции, которые ни в коем
случае не должны прерваться.
Для того чтобы запустить поток с нужным приоритетом, необходимо передать одно
из приведенных выше значений в метод start(). Например:
CustomThread thread;
thread.start(QThread::IdlePriority);
http://www.slideshare.net/IgorShkulipa 13
Синхронизация
Основные сложности возникают тогда, когда потокам нужно совместно
использовать одни и те же данные. Так как несколько потоков могут
одновременно обращаться и записывать данные в одну область, то это
может привести к нежелательным последствиям.
Синхронизация позволяет задавать критические секции (critical
sections), к которым в определенный момент имеет доступ только один
из потоков. Это гарантирует то, что данные ресурса, контролируемые
критической секцией, будут невидимы другими потоками и они не
изменят их. И только после того, как поток выполнит всю
необходимую работу, он освобождает ресурс, и, затем, доступ к этому
ресурсу может получить любой другой поток.
Например, если один поток записывает информацию в файл, то все
другие не смогут использовать этот файл до тех пор, пока поток не
освободит его.
http://www.slideshare.net/IgorShkulipa 14
Объекты синхронизации Mutex
Мьютексы (mutex) обеспечивают взаимоисключающий доступ к ресурсам,
гарантирующий то, что критическая секция будет обрабатываться только одним
потоком. Поток, владеющий мьютексом, обладает эксклюзивным правом на
использование ресурса, защищенного мьютексом, и другой поток не может
завладеть уже занятым мьютексом.
Механизм мьютексов реализован классом QMutex. Метод lock() класса QMutex
производит блокировку ресурса. Для обратной операции существует метод
unlock(), который открывает закрытый ресурс для других потоков.
Класс QMutex также содержит метод tryLock(). Этот метод можно использовать для
того, чтобы проверить, заблокирован ресурс или нет. Этот метод не
приостанавливает исполнение потока и возвращается немедленно, со значением
false, если ресурс уже захвачен другим потоком, и не ожидает его
освобождения. В случае успешного захвата ресурса этот метод вернет true.
В сложных функциях, особенно при использовании исключений C++, легко можно
ошибиться при выполнении последовательностей операций по
запиранию/отпиранию мьютексов. Поэтому, в состав Qt включен класс
QMutexLocker, который значительно упрощает работу с мьютексами.
Конструктор класса QMutexLocker принимает объект QMutex в виде
аргумента и запирает его. Деструктор класса QMutexLocker - отпирает
мьютекс.
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;
};
http://www.slideshare.net/IgorShkulipa 16
QWaitCondition
Библиотека Qt предоставляет класс QWaitCondition, обеспечивающий
возможность координации потоков.
Если поток намеревается дождаться разблокировки ресурса, то он
вызывает метод QWaitCondition::wait() и, тем самым, входит в
режим ожидания. Выводится он из этого режима в том случае, если
поток, который заблокировал ресурс, вызовет метод
QWaitCondition::wakeOne() или QWaitCondition::wakeAll().
Разница этих двух методов в том, что первый выводит из состояния
ожидания только один поток, а второй — все сразу. Также для потока
можно установить время, в течение которого он может ожидать
разблокировки данных. Для этого нужно передать в метод wait()
целочисленное значение, обозначающее временной интервал в
миллисекундах.
http://www.slideshare.net/IgorShkulipa 17
Семафоры
Семафоры являются обобщением мьютексов. Как и мьютексы, они
служат для защиты критических секций, чтобы доступ к ним
одновременно могло иметь определенное число потоков. Все другие
потоки обязаны ждать.
Предположим, что программа поддерживает пять ресурсов одного и того
же типа, одновременный доступ к которым может быть предоставлен
только пяти потокам. Как только все пять ресурсов будут
заблокированы, следующий поток, запрашивающий ресурс данного
типа, будет приостановлен до освобождения одного из них.
Принцип действия семафоров очень прост. Они начинают действовать с
установленного значения счетчика. Каждый раз, когда поток получает
право на владение ресурсом, значение этого счетчика уменьшается на
единицу. И наоборот, когда поток уступает право владения этим
ресурсом, счетчик увеличивается на единицу. При значении счетчика
равном нулю семафор становится недоступным. Механизм семафоров
реализует класс QSemaphore. Счетчик устанавливается в
конструкторе при создании объекта этого класса.
http://www.slideshare.net/IgorShkulipa 18
Взаимная блокировка
При работе с многопоточностью, возможна
такая ситуация, когда поток
заблокировал ресурс А, а после
работы над ним собирается работать с
ресурсом В. Другой же поток
заблокировал ресурс В и по окончании
намеревается работать с ресурсом А. И
вот один из потоков, закончив работу,
обнаружил, что нужный ему ресурс
заблокирован другим потоком. Он
переходит в режим ожидания, надеясь
дождаться разблокировки ресурса, но
то же самое делает и другой поток.
В итоге — оба ждут друг друга. Если ни один из этих потоков не освободит занятый
им ресурс, то оба "зависнут" и не смогут продолжать свою работу дальше.
Это явление получило название взаимной блокировки (deadlock).
Существует множество решений такой проблемы. Например, можно так организовать
работу потока, чтобы, в том случае, если поток не сможет получить доступ к
необходимому ресурсу, он просто произвел бы освобождение занятых им
ресурсов, а позже повторил попытку захвата необходимых ресурсов.
http://www.slideshare.net/IgorShkulipa 19
Обмен сообщениями между потоками
Один из важнейших вопросов при многопоточном программировании —
это обмен сообщениями.
Каждый поток может иметь свой собственный цикл событий. Благодаря
этому можно осуществлять связь между объектами. Такая связь может
производиться двумя способами: при помощи соединения сигналов и
слотов или обмена событиями.
Класс QObject реализован так, что обладает близостью к потокам.
Каждый объект, произведенный от унаследованного от QObject
класса, располагает ссылкой на поток, в котором он был создан. Эту
ссылку можно получить вызовом метода QObject::thread(). Потоки
осведомляют свои объекты. Благодаря этому каждый объект знает, к
какому потоку он принадлежит.
Обработка событий производится из контекста принадлежности объекта к
потоку, то есть обработка его событий будет производиться в том
потоке, которому объект принадлежит. Объекты можно перемещать из
одного потока в другой с помощью метода
QObject::moveToThread().
http://www.slideshare.net/IgorShkulipa 20
Отправка событий
Отправка событий — это еще одна из возможностей для осуществления
связи между объектами. Есть два метода для высылки событий -
QCoreApplication::postEvent() и QCoreApplication::sendEvent().
Для того чтобы объект потока был в состоянии обрабатывать получаемые
события, в классе потока нужно реализовать метод QObject::event().
Если поток предназначен исключительно для отправки событий, а не для
их получения, то реализацию методов обработки событий и запуск
цикла обработки событий можно опустить.
http://www.slideshare.net/IgorShkulipa 21
Сигналы и слоты
Можно взять сигнал объекта одного потока и соединить его со слотом
объекта другого потока. Как мы уже знаем, соединение с помощью
метода connect() предоставляет дополнительный параметр,
обозначающий режим обработки и равный, по умолчанию, значению
Qt::AutoConnection, которое соответствует автоматическому режиму.
Как только происходит высылка сигнала, Qt проверяет — происходит
связь в одном и том же или разных потоках. Если это один и тот же
поток, то высылка сигнала приведет к прямому вызову метода. В том
случае, если это разные потоки, сигнал будет преобразован в событие
и доставлен нужному объекту.
Сигналы и слоты в Qt реализованы с механизмом надежности работы в
потоках, а это означает, что вы можете высылать сигналы и получать,
не заботясь о блокировке ресурсов.
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
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();
}
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);
}
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();
}
http://www.slideshare.net/IgorShkulipa 26
Результат
http://www.slideshare.net/IgorShkulipa 27
Лабораторная работа №8. Многопоточность
Создать в тетрисе дополнительный поток, который с определенной
периодичностью будет обновлять рейтинг пользователей на форме
(см. Лабораторную работу №5).

More Related Content

PDF
C++ STL & Qt. Занятие 11.
PDF
C++ STL & Qt. Занятие 03.
PDF
C++ STL & Qt. Занятие 04.
PDF
C++ STL & Qt. Занятие 05.
PDF
C++ STL & Qt. Занятие 07.
PDF
C++ STL & Qt. Занятие 01.
PDF
C++ STL & Qt. Занятие 02.
PDF
C++ STL & Qt. Занятие 10.
C++ STL & Qt. Занятие 11.
C++ STL & Qt. Занятие 03.
C++ STL & Qt. Занятие 04.
C++ STL & Qt. Занятие 05.
C++ STL & Qt. Занятие 07.
C++ STL & Qt. Занятие 01.
C++ STL & Qt. Занятие 02.
C++ STL & Qt. Занятие 10.

What's hot (20)

PDF
C++ STL & Qt. Занятие 06.
PDF
C++ STL & Qt. Занятие 09.
PPTX
Java Core. Lecture# 5. Concurrency.
PPTX
Java Core. Lecture# 3. Part# 3. Multithreading.
PPT
Java. Многопоточность.
PDF
Java осень 2014 занятие 3
PDF
Java осень 2014 занятие 5
PDF
Java осень 2014 занятие 6
PPTX
Java Core. Lecture# 3. Part# 2. Exceptions.
PPT
Ввведение в java
PPTX
Java Core. Lecture# 1. Intro
PPT
Введение в сетевые технологии
PDF
C# Desktop. Занятие 01.
PDF
C# Desktop. Занятие 02.
PPTX
Java Core. Lecture# 3. Part# 1. Abstract classes.
PPT
Java. Сборщик мусора. Работа с памятью.
PPT
Системы контроля версий
PPTX
Bytecode
PDF
Java осень 2014 занятие 8
PDF
C++ Базовый. Занятие 15.
C++ STL & Qt. Занятие 06.
C++ STL & Qt. Занятие 09.
Java Core. Lecture# 5. Concurrency.
Java Core. Lecture# 3. Part# 3. Multithreading.
Java. Многопоточность.
Java осень 2014 занятие 3
Java осень 2014 занятие 5
Java осень 2014 занятие 6
Java Core. Lecture# 3. Part# 2. Exceptions.
Ввведение в java
Java Core. Lecture# 1. Intro
Введение в сетевые технологии
C# Desktop. Занятие 01.
C# Desktop. Занятие 02.
Java Core. Lecture# 3. Part# 1. Abstract classes.
Java. Сборщик мусора. Работа с памятью.
Системы контроля версий
Bytecode
Java осень 2014 занятие 8
C++ Базовый. Занятие 15.
Ad

Viewers also liked (20)

PPTX
Vijay Bhosekar_ PP_Rodale Institute_Feb 9
PDF
PPC Masters February 2015
PDF
C# Web. Занятие 03.
PPTX
Mid map gaya belajar
PPTX
Production diary 20
PDF
Emergency Assistance
PDF
C# Web. Занятие 12.
PPTX
Vijay Bhosekar_ PP_Rodale Institute_Feb 9
PDF
Vijay bhosekar published article organic agriculture
PPTX
Laura mulvey’s male gaze theory
PPTX
Advanced LinkedIn Techniques
PDF
Vijay Bhosekar_ Research Article_ Frontiers in Plant Science
PPTX
Fotos de la tesis
PPTX
psychiatry
DOC
Updated Colorado Resume
PPTX
Production diary 15
PPTX
Apokries2015
PDF
C# Web. Занятие 10.
PPTX
What is Lean UX?
DOCX
Effortless Energy Outreach Plan
Vijay Bhosekar_ PP_Rodale Institute_Feb 9
PPC Masters February 2015
C# Web. Занятие 03.
Mid map gaya belajar
Production diary 20
Emergency Assistance
C# Web. Занятие 12.
Vijay Bhosekar_ PP_Rodale Institute_Feb 9
Vijay bhosekar published article organic agriculture
Laura mulvey’s male gaze theory
Advanced LinkedIn Techniques
Vijay Bhosekar_ Research Article_ Frontiers in Plant Science
Fotos de la tesis
psychiatry
Updated Colorado Resume
Production diary 15
Apokries2015
C# Web. Занятие 10.
What is Lean UX?
Effortless Energy Outreach Plan
Ad

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

PDF
C# Desktop. Занятие 15.
PPT
8. java lecture threads
PPTX
javaaaaddawdawdawdasdsadsaddadadm11n.pptx
PPTX
объектно ориентированная платформа для построения
PPTX
Как пройти собеседование и получить первую работу на Swift
PPT
Usage concurrence in java
PDF
C++ осень 2013 лекция 4
PDF
Java весна 2014 лекция 5
PDF
C++ Базовый. Занятие 17.
PDF
Архитектура. Доступноять программных систем.
PDF
JavaScript Базовый. Занятие 07.
PDF
Многопоточность, работа с сетью (Lecture 12 – multithreading, network)
PDF
Android осень 2013 лекция 6
PPTX
Классы и объекты в Java
PPTX
Working with .NET Threads
PDF
C# Desktop. Занятие 06.
PPTX
Programming Java - Lection 06 - Multithreading - Lavrentyev Fedor
PPTX
Mikhail Valkov_Antipatterns
PDF
Java 9: what is there beyond modularization
PPTX
Асинхронность и сопрограммы
C# Desktop. Занятие 15.
8. java lecture threads
javaaaaddawdawdawdasdsadsaddadadm11n.pptx
объектно ориентированная платформа для построения
Как пройти собеседование и получить первую работу на Swift
Usage concurrence in java
C++ осень 2013 лекция 4
Java весна 2014 лекция 5
C++ Базовый. Занятие 17.
Архитектура. Доступноять программных систем.
JavaScript Базовый. Занятие 07.
Многопоточность, работа с сетью (Lecture 12 – multithreading, network)
Android осень 2013 лекция 6
Классы и объекты в Java
Working with .NET Threads
C# Desktop. Занятие 06.
Programming Java - Lection 06 - Multithreading - Lavrentyev Fedor
Mikhail Valkov_Antipatterns
Java 9: what is there beyond modularization
Асинхронность и сопрограммы

More from Igor Shkulipa (20)

PDF
Общие темы. Тема 03.
PDF
Общие темы. Тема 02.
PDF
Общие темы. Тема 01.
PDF
JavaScript Базовый. Занятие 06.
PDF
JavaScript Базовый. Занятие 11.
PDF
JavaScript Базовый. Занятие 09.
PDF
JavaScript Базовый. Занятие 10.
PDF
JavaScript Базовый. Занятие 05.
PDF
JavaScript Базовый. Занятие 08.
PDF
JavaScript Базовый. Занятие 01.
PDF
JavaScript Базовый. Занятие 04.
PDF
JavaScript Базовый. Занятие 03.
PDF
JavaScript Базовый. Занятие 02.
PDF
C# Web. Занятие 09.
PDF
C# Web. Занятие 08.
PDF
C# Web. Занятие 07.
PDF
C# Web. Занятие 04.
PDF
C# Web. Занятие 16.
PDF
C# Web. Занятие 14.
PDF
C# Web. Занятие 15.
Общие темы. Тема 03.
Общие темы. Тема 02.
Общие темы. Тема 01.
JavaScript Базовый. Занятие 06.
JavaScript Базовый. Занятие 11.
JavaScript Базовый. Занятие 09.
JavaScript Базовый. Занятие 10.
JavaScript Базовый. Занятие 05.
JavaScript Базовый. Занятие 08.
JavaScript Базовый. Занятие 01.
JavaScript Базовый. Занятие 04.
JavaScript Базовый. Занятие 03.
JavaScript Базовый. Занятие 02.
C# Web. Занятие 09.
C# Web. Занятие 08.
C# Web. Занятие 07.
C# Web. Занятие 04.
C# Web. Занятие 16.
C# Web. Занятие 14.
C# Web. Занятие 15.

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

  • 1. Темы лекции: Многопоточность в Qt. Практическое задание: Многопоточность в Qt. Тренер: Игорь Шкулипа, к.т.н. С++ Библиотеки STL и Qt. Занятие 8
  • 2. http://www.slideshare.net/IgorShkulipa 2 Object Pool Применение паттерна Object Pool может значительно повысить производительность системы; его использование наиболее эффективно в ситуациях, когда создание экземпляров некоторого класса требует больших затрат, объекты в системе создаются часто, но число создаваемых объектов в единицу времени ограничено. Пулы объектов (известны также как пулы ресурсов) используются для управления кэшированием объектов. Клиент, имеющий доступ к пулу объектов может избежать создания новых объектов, просто запрашивая в пуле уже созданный экземпляр. Пул объектов может быть растущим, когда при отсутствии свободных создаются новые объекты или c ограничением количества создаваемых объектов.
  • 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. 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. 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. 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. 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. http://www.slideshare.net/IgorShkulipa 8 Преимущества и недостатки ◦ Пул объектов отслеживает объекты, которые он создает. ◦ Паттерн Object Pool может использоваться для инкапсуляции логики создания объектов. Однако он не управляет ими после их создания. ◦ Достоинством этого паттерна является быстрое создание объектов, однако это реализовано за счет использования больших ресурсов памяти.
  • 9. http://www.slideshare.net/IgorShkulipa 9 Процессы и потоки Процессы представляют собой программы, независимые друг от друга и загруженные для исполнения. Каждый процесс должен создавать хотя бы один поток, называемый основным. Основной поток процесса создается в момент запуска программы. Однако сам процесс может создавать несколько потоков одновременно. Многопоточность позволяет разделять задачи и независимо работать над каждой из них для того, чтобы максимально эффективно задействовать процессор. Написание многопоточных приложений требует больше времени и усложняет процесс отладки, поэтому многопоточность нужно применять тогда, когда это действительно необходимо. Многопоточность удобно использовать для того, чтобы блокировка или зависание одного из методов не стали причиной нарушения функционирования основной программы.
  • 10. http://www.slideshare.net/IgorShkulipa 10 QProcess Процесс — это экземпляр программы, загруженной в память компьютера для выполнения. Создание процесса может оказаться полезным для использования функциональных возможностей программ, не имеющих графического интерфейса и работающих с командной строкой. Другое полезное свойство — довольно простой запуск других программ из текущей программы. Процессы можно создавать с помощью класса QProcess, который определен в заголовочном файле QProcess. Благодаря тому, что этот класс унаследован от класса QIODevice, объекты этого класса в состоянии считывать информацию, выводимую запущенными процессами, и даже подтверждать их запросы на ввод информации. Этот класс содержит методы для манипулирования системными переменными процесса. Работа с объектами класса QProcess производится в асинхронном режиме, что позволяет сохранять работоспособность графического интерфейса программы в моменты, когда запущенные процессы находятся в работе.
  • 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. http://www.slideshare.net/IgorShkulipa 12 Приоритет потоков У каждого потока есть приоритет, указывающий процессору, как должно протекать выполнение потока по отношению к другим потокам. Приоритеты разделяются по группам: • в первую входят четыре наиболее часто применяемых приоритета. Их значимость распределяется по возрастанию — IdlePriority, LowestPriority, LowPriority, NormaiPriority. Они подходят для решения задач, которым процессор требуется только время от времени, например, для фоновой печати или для каких-нибудь несрочных действий; • во вторую группу входят два приоритета — HighPriority, HighestPriority. Пользуйтесь такими приоритетами с большой осторожностью. Обычно эти потоки большую часть времени ожидают какие-либо события; • в третью входят два приоритета — TimeCriticalPriority, InheritPriority. Потоки с этими приоритетами нужно создавать в случаях крайней необходимости. Эти приоритеты нужны для программ, напрямую общающихся с аппаратурой или выполняющих операции, которые ни в коем случае не должны прерваться. Для того чтобы запустить поток с нужным приоритетом, необходимо передать одно из приведенных выше значений в метод start(). Например: CustomThread thread; thread.start(QThread::IdlePriority);
  • 13. http://www.slideshare.net/IgorShkulipa 13 Синхронизация Основные сложности возникают тогда, когда потокам нужно совместно использовать одни и те же данные. Так как несколько потоков могут одновременно обращаться и записывать данные в одну область, то это может привести к нежелательным последствиям. Синхронизация позволяет задавать критические секции (critical sections), к которым в определенный момент имеет доступ только один из потоков. Это гарантирует то, что данные ресурса, контролируемые критической секцией, будут невидимы другими потоками и они не изменят их. И только после того, как поток выполнит всю необходимую работу, он освобождает ресурс, и, затем, доступ к этому ресурсу может получить любой другой поток. Например, если один поток записывает информацию в файл, то все другие не смогут использовать этот файл до тех пор, пока поток не освободит его.
  • 14. http://www.slideshare.net/IgorShkulipa 14 Объекты синхронизации Mutex Мьютексы (mutex) обеспечивают взаимоисключающий доступ к ресурсам, гарантирующий то, что критическая секция будет обрабатываться только одним потоком. Поток, владеющий мьютексом, обладает эксклюзивным правом на использование ресурса, защищенного мьютексом, и другой поток не может завладеть уже занятым мьютексом. Механизм мьютексов реализован классом QMutex. Метод lock() класса QMutex производит блокировку ресурса. Для обратной операции существует метод unlock(), который открывает закрытый ресурс для других потоков. Класс QMutex также содержит метод tryLock(). Этот метод можно использовать для того, чтобы проверить, заблокирован ресурс или нет. Этот метод не приостанавливает исполнение потока и возвращается немедленно, со значением false, если ресурс уже захвачен другим потоком, и не ожидает его освобождения. В случае успешного захвата ресурса этот метод вернет true. В сложных функциях, особенно при использовании исключений C++, легко можно ошибиться при выполнении последовательностей операций по запиранию/отпиранию мьютексов. Поэтому, в состав Qt включен класс QMutexLocker, который значительно упрощает работу с мьютексами. Конструктор класса QMutexLocker принимает объект QMutex в виде аргумента и запирает его. Деструктор класса QMutexLocker - отпирает мьютекс.
  • 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. http://www.slideshare.net/IgorShkulipa 16 QWaitCondition Библиотека Qt предоставляет класс QWaitCondition, обеспечивающий возможность координации потоков. Если поток намеревается дождаться разблокировки ресурса, то он вызывает метод QWaitCondition::wait() и, тем самым, входит в режим ожидания. Выводится он из этого режима в том случае, если поток, который заблокировал ресурс, вызовет метод QWaitCondition::wakeOne() или QWaitCondition::wakeAll(). Разница этих двух методов в том, что первый выводит из состояния ожидания только один поток, а второй — все сразу. Также для потока можно установить время, в течение которого он может ожидать разблокировки данных. Для этого нужно передать в метод wait() целочисленное значение, обозначающее временной интервал в миллисекундах.
  • 17. http://www.slideshare.net/IgorShkulipa 17 Семафоры Семафоры являются обобщением мьютексов. Как и мьютексы, они служат для защиты критических секций, чтобы доступ к ним одновременно могло иметь определенное число потоков. Все другие потоки обязаны ждать. Предположим, что программа поддерживает пять ресурсов одного и того же типа, одновременный доступ к которым может быть предоставлен только пяти потокам. Как только все пять ресурсов будут заблокированы, следующий поток, запрашивающий ресурс данного типа, будет приостановлен до освобождения одного из них. Принцип действия семафоров очень прост. Они начинают действовать с установленного значения счетчика. Каждый раз, когда поток получает право на владение ресурсом, значение этого счетчика уменьшается на единицу. И наоборот, когда поток уступает право владения этим ресурсом, счетчик увеличивается на единицу. При значении счетчика равном нулю семафор становится недоступным. Механизм семафоров реализует класс QSemaphore. Счетчик устанавливается в конструкторе при создании объекта этого класса.
  • 18. http://www.slideshare.net/IgorShkulipa 18 Взаимная блокировка При работе с многопоточностью, возможна такая ситуация, когда поток заблокировал ресурс А, а после работы над ним собирается работать с ресурсом В. Другой же поток заблокировал ресурс В и по окончании намеревается работать с ресурсом А. И вот один из потоков, закончив работу, обнаружил, что нужный ему ресурс заблокирован другим потоком. Он переходит в режим ожидания, надеясь дождаться разблокировки ресурса, но то же самое делает и другой поток. В итоге — оба ждут друг друга. Если ни один из этих потоков не освободит занятый им ресурс, то оба "зависнут" и не смогут продолжать свою работу дальше. Это явление получило название взаимной блокировки (deadlock). Существует множество решений такой проблемы. Например, можно так организовать работу потока, чтобы, в том случае, если поток не сможет получить доступ к необходимому ресурсу, он просто произвел бы освобождение занятых им ресурсов, а позже повторил попытку захвата необходимых ресурсов.
  • 19. http://www.slideshare.net/IgorShkulipa 19 Обмен сообщениями между потоками Один из важнейших вопросов при многопоточном программировании — это обмен сообщениями. Каждый поток может иметь свой собственный цикл событий. Благодаря этому можно осуществлять связь между объектами. Такая связь может производиться двумя способами: при помощи соединения сигналов и слотов или обмена событиями. Класс QObject реализован так, что обладает близостью к потокам. Каждый объект, произведенный от унаследованного от QObject класса, располагает ссылкой на поток, в котором он был создан. Эту ссылку можно получить вызовом метода QObject::thread(). Потоки осведомляют свои объекты. Благодаря этому каждый объект знает, к какому потоку он принадлежит. Обработка событий производится из контекста принадлежности объекта к потоку, то есть обработка его событий будет производиться в том потоке, которому объект принадлежит. Объекты можно перемещать из одного потока в другой с помощью метода QObject::moveToThread().
  • 20. http://www.slideshare.net/IgorShkulipa 20 Отправка событий Отправка событий — это еще одна из возможностей для осуществления связи между объектами. Есть два метода для высылки событий - QCoreApplication::postEvent() и QCoreApplication::sendEvent(). Для того чтобы объект потока был в состоянии обрабатывать получаемые события, в классе потока нужно реализовать метод QObject::event(). Если поток предназначен исключительно для отправки событий, а не для их получения, то реализацию методов обработки событий и запуск цикла обработки событий можно опустить.
  • 21. http://www.slideshare.net/IgorShkulipa 21 Сигналы и слоты Можно взять сигнал объекта одного потока и соединить его со слотом объекта другого потока. Как мы уже знаем, соединение с помощью метода connect() предоставляет дополнительный параметр, обозначающий режим обработки и равный, по умолчанию, значению Qt::AutoConnection, которое соответствует автоматическому режиму. Как только происходит высылка сигнала, Qt проверяет — происходит связь в одном и том же или разных потоках. Если это один и тот же поток, то высылка сигнала приведет к прямому вызову метода. В том случае, если это разные потоки, сигнал будет преобразован в событие и доставлен нужному объекту. Сигналы и слоты в Qt реализованы с механизмом надежности работы в потоках, а это означает, что вы можете высылать сигналы и получать, не заботясь о блокировке ресурсов.
  • 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. 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. 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. 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(); }
  • 27. http://www.slideshare.net/IgorShkulipa 27 Лабораторная работа №8. Многопоточность Создать в тетрисе дополнительный поток, который с определенной периодичностью будет обновлять рейтинг пользователей на форме (см. Лабораторную работу №5).