Рассмотрим создание потоков на языке Visual C++. На примере программы в Visual Studio разберем основы и применение необходимых классов. В конце урока вы найдете видео, в котором тема раскрывается более подробно.
Потоки в Visual C++ — Создание демонстрационной программы
Для примера создадим программу, в которой круг будет перемещаться по периметру прямоугольной области изображения. Перемещение круга будет обрабатываться в отдельном потоке.
Если в данном случае не использовать поток, то весь интерфейс программы «повиснет» во время выполнения процесса движения круга и нельзя будет нажать ни на одну кнопку на окне до момента завершения перемещения круга.
Создадим проект Windows Forms в Visual Studio на языке Visual C++. Сформируем из элементов управления интерфейс. Разместим на форме две кнопки Button и компонент для вывода изображения PictureBox.
Двойным щелчком по каждой из кнопок сформируем обработчики события нажатия по ним.
У формы создадим обработчик события, возникающего при ее закрытии (непосредственно перед закрытием).
Напишем код метода, который будет заниматься отрисовкой круга и который будет выполняться в отдельном потоке.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 |
// данный метод будет исполняться в отдельном потоке public: void Go() { // делаем недоступной кнопку startButton startButton->BeginInvoke(gcnew InvokeDelegateStartButtonEnabled(this, &MyForm::SetStartButtonEnabled), false); // радиус круга int R = 30; // создаем изображение Bitmap^ image = gcnew Bitmap(pictureBox->Width, pictureBox->Height); // получаем объект для рисования Graphics^ g = Graphics::FromImage(image); // перемещение: левый верхний угол -> правый верхний угол for (int x = 0; x <= pictureBox->Width - 2 * R; x += 2) { // заливка изображения белым цветом g->Clear(Color::White); // рисование окружности на изображении g->FillEllipse(Brushes::Aqua, x, 0, 2 * R, 2 * R); // вывод изображения в pictureBox pictureBox->BeginInvoke(gcnew InvokeDelegateImageToPictureBox(this, &MyForm::SetImageToPictureBox), image); // остановка потока на 10 мс Thread::Sleep(10); } // правый верхний -> правый нижний for (int y = 0; y <= pictureBox->Height - 2 * R; y += 2) { g->Clear(Color::White); g->FillEllipse(Brushes::Aqua, pictureBox->Width - 2 * R, y, 2 * R, 2 * R); pictureBox->BeginInvoke(gcnew InvokeDelegateImageToPictureBox(this, &MyForm::SetImageToPictureBox), image); Thread::Sleep(10); } // правый нижний -> левый нижний for (int x = pictureBox->Width - 2 * R; x >= 0; x -= 2) { g->Clear(Color::White); g->FillEllipse(Brushes::Aqua, x, pictureBox->Height - 2 * R, 2 * R, 2 * R); pictureBox->BeginInvoke(gcnew InvokeDelegateImageToPictureBox(this, &MyForm::SetImageToPictureBox), image); Thread::Sleep(10); } // левый нижний -> левый верхний for (int y = pictureBox->Height - 2 * R; y >= 0; y -= 2) { g->Clear(Color::White); g->FillEllipse(Brushes::Aqua, 0, y, 2 * R, 2 * R); pictureBox->BeginInvoke(gcnew InvokeDelegateImageToPictureBox(this, &MyForm::SetImageToPictureBox), image); Thread::Sleep(10); } // делаем доступной кнопку startButton startButton->BeginInvoke(gcnew InvokeDelegateStartButtonEnabled(this, &MyForm::SetStartButtonEnabled), true); } |
Так как обращение к кнопкам и PictureBox из отдельного потока невозможно (поскольку они были созданы не в нем, а другом — главном потоке программы), необходимо использовать делегаты.
Делегат — это класс, содержащий ссылку на метод, который необходимо выполнить. Метод при этом содержит операции с конкретными экземплярами объектов.
Объявим делегаты и дополнительные методы для них:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
// делегат для вывода изображения в pictureBox delegate void InvokeDelegateImageToPictureBox(Bitmap^ image); // метод выводит изображение в pictureBox public: void SetImageToPictureBox(Bitmap^ image) { pictureBox->Image = image; } // делегат для изменения свойства Enabled у startButton delegate void InvokeDelegateStartButtonEnabled(bool enabled); // метод устанавливает свойство Enabled для startButton public: void SetStartButtonEnabled(bool enabled) { startButton->Enabled = enabled; } |
Для создания и выполнения потока в Visual C++ .NET используется класс Thread. В классе формы создадим поле данного типа.
1 |
private: Thread^ myThread; |
В обработчике кнопки «Старт» напишем код, создающий и запускающий на выполнение поток, выполняющий код реализованного нами метода «Go».
1 2 3 4 5 |
private: System::Void startButton_Click(System::Object^ sender, System::EventArgs^ e) { // создаем и запускаем поток myThread = gcnew Thread(gcnew ThreadStart(this, &MyForm::Go)); myThread->Start(); } |
В обработчик кнопки «Выход» добавим код, завершающий поток (на случай если кнопка будет нажата во время выполнения потока) и закрывающий форму.
1 2 3 4 5 6 7 8 9 10 |
private: System::Void exitButton_Click(System::Object^ sender, System::EventArgs^ e) { // завершаем поток try { myThread->Abort(); } catch (...) {} // закрываем форму this->Close(); } |
В обработчик события закрытия формы также добавим код, принудительно завершающий поток.
1 2 3 4 5 6 7 |
private: System::Void MyForm_FormClosing(System::Object^ sender, System::Windows::Forms::FormClosingEventArgs^ e) { // завершаем поток try { myThread->Abort(); } catch (...) {} } |
Запустим программу и протестируем ее.
Исходник программы доступен для скачивания по ссылке ниже.
Скачать исходник
Потоки в Visual C++ — Видеоурок
Поделиться в соц. сетях: