Потоки. Синхронизирующие объекты Windows

Страницы работы

Содержание работы

Практическое занятие. Потоки. Синхронизирующие объекты Windows.

1. Тестовое приложение Qt

Создайте GUI-приложение на базе QMainWindow.

Для запуска потоков достаточно создать набор кнопок (кроме обычных PushButton, для задания приоритетов потоков возможно, понадобятся еще кнопки-переключатели - RadioButton), а всю оставшуюся клиентскую область окна предоставить для демонстрации взаимодействия потоков.

Каждый поток будет «рисовать» точки:

·  своим цветом

·  на своем уровне в окне (координата “y” будет у каждого потока своя)

·  все потоки будут использовать (разделять) общую координату “x” для того, чтобы было видно, как ОС переключает потоки

2. Структура (класс) для передачи данных потоковой функции

Чтобы что-нибудь подобное можно было бы нарисовать в потоковой функции, ей нужно передать довольно много параметров, поэтому логично в качестве параметра передавать указатель на объект Вашего класса или структуры, в который можно запаковать все необходимые данные.

class MyData

{

HWND hw; //дескриптор окна (для того, чтобы окну можно было посылать сообщения из потоковой функции)

int y; //индивидуальная для потока координата Y

QColor col; //индивидуальный цвет

int maxX; // «докуда» рисовать

HANDLE hObject; //дескриптор синхронизирующего объекта

};

Подсказка: как можно/нужно формировать данные для передачи в потоковую функцию?

3. Вспомогательный класс для хранения

Так как рисуемые потоками точки будут разного цвета, с разными координатами – создаем вспомогательный класс MyPoint:

  • Цвета точки
  • Координаты точки

4. Для отрисовки данных

Данные:

  • формируются потоками
  • отрисовываются главным окном приложения

=>

1. В классе MainWindow предусмотреть переменную для приема/хранения данных – контейнер, например static QVector<MyPoint> vPoints; . Почему лучше static???

2. Если потоковая функция является статическим методом класса, то имеет доступ ко всем статическим данным класса => в потоковой функции:

  • Формируем очередную точку
  • Добавляем ее в контейнер
  • Заставляем окно перерисоваться

3. Чтобы из потоковой функции вызвать перерисовку окна:

  • Нельзя вызвать метод repaint() непосредственно, так как этот вызов будет сделан из ДРУГОГО потока
  • Нужно, обладая дескриптором окна, послать ему пользовательское сообщение PostMessage() (которое необязательно регистрировать, так как взаимодействие происходит внутри одного приложения. Важно, чтобы значение идентификатора сообщения было >= WM_APP)!
  • При получении сообщения в перегруженном виртуальном методе

bool QWidget::winEvent ( MSG * message, long * result ) [virtual protected]

вызвать перерисовку окна

2. Для собственно рисования перегрузите в классе MainWindow метод void QWidget::paintEvent ( QPaintEvent * event ) [virtual protected]

, в котором нарисуйте все точки контейнера vPoints

5. Общие замечания по оформлению потоковой функции

  1. Потоковая функция не может быть обычным методом класса! Почему??? Зато может быть статическим методом класса
  2. Вид потоковой функции предопределен: DWORD WINAPI MainWindow::ThreadFunc( LPVOID lpParam ){}
  3. В потоковой функции Вам понадобится задержка (для того, чтобы один поток не изрисовал всю отведенную область за свой квант времени):
    1. величину задержки подбираете исходя из возможностей Вашего процессора
    2. задержку не стоит оформлять как цикл с пустым телом, так как оптимизирующий компилятор такую задержку скорее всего «выкинет» => имитация деятельности
  4.  

6. Запуск рабочих потоков без синхронизации.

6.1. Запуск потоков с одинаковым приоритетом

Требуется запустить несколько потока с одинаковым приоритетом  и «увидеть» как операционная система переключает потоки в соответствии с отведенными каждому потоку квантами времени.

Запуск потоков – системная Win32 API функция CreateThread ()

HANDLE CreateThread(//возвращаемое значение==0 - ошибка

LPSECURITY_ATTRIBUTES lpsa, // 0- защита по умолчанию и дочерние процессы не наследуют данный поток

SIZE_T cbStack, // размер стека или 0 (по умолчанию)

LPTHREAD_START_ROUTINE lpStartAddr, // указатель на функцию (точку входа) потока - DWORD WINAPI ThreadF(LPVOID lpvThreadParm); Не может быть NULL! Каждый поток внутри родительского процесса начинает свое выполнение с вызова специальной функции, называемой потоковой функцией. Выполнение потока продолжается до тех пор, пока не завершится его потоковая функция.

LPVOID lpvThreadParm, // значение или указатель на любые данные. Используется по замыслу программиста. Эти данные должны гарантированно существовать пока их использует потоковая функция, так как стек у каждого потока свой!!!

DWORD fdwCreate, // дополнительные флаги создания: 0 (начинает выполняться немедленно) CREATE_SUSPENDED - поток создается “остановленным”, то есть ожидающим запуска. Для того чтобы запустить такой поток, кто-то должен вызвать функцию ResumeThread()

LPDWORD lpIDThread// сюда функция вернет идентификатор (в NT – если Вам не нужно значение, можно передать 0)

Похожие материалы

Информация о работе