Немного про потоки в C++/CLI
Возможность выполнять программу сразу в нескольких потоках - ценное свойство современных языков программирования. Увы, там есть масса нюансов и если не нужно какое-то особое "ручное" управление потоками, то уместнее всё делать на C#, а не на C++/CLI. На C++/CLI также нужно явное подключение к потоку функций-делегатов, так как анонимных делегатов в нём, в отличие от C#, нет.
1. В первой программке мы решаем следующую задачу - создать в приложении два потока, назначение одного из них - периодическое чтение системного времени и заполнение глобальной структуры DateTime
(часы, минуты, секунды), второй поток отвечает за вывод данной структуры в форму приложения. При старте такой программы требуется чуть больше времени, чем обычно (на создание потоков).
Вот основная часть кода, на форме расположена и настроена только метка Label^ label1
:
using namespace System::Threading; //подключить в секции namespace //... //инициализировать в конструкторе формы SetTimeThread = gcnew Thread(gcnew ThreadStart(this, &MyForm::SetTime)); WriteTimeThread = gcnew Thread(gcnew ThreadStart(this, &MyForm::WriteTime)); SetTimeThread->IsBackground = true; WriteTimeThread->IsBackground = true; SetTimeThread->Start(); WriteTimeThread->Start(); //... #pragma endregion //сервисные функции void SetText() { label1->Text = dateTime.ToString("HH:mm:ss"); } void SetTime() { while (true) { dateTime = DateTime::Now; Thread::Sleep(100); } } void WriteTime() { try { while (true) { if (this->InvokeRequired) { this->Invoke(gcnew Action(this, &MyForm::SetText)); } else SetText(); } } catch (...) {} } private: System::Threading::Thread^ SetTimeThread; //Поток 1 private: System::Threading::Thread^ WriteTimeThread; //Поток 2 private: DateTime dateTime; private: System::Windows::Forms::Label^ label1;
2. Во втором примере мы обходимся с двумя одновременно работающими потоками при помощи Invoke
и Action
, причём, цикл работы обоих потоков ограничен, а запуска повторных процессов мы избегаем проверкой состояния потока:
using namespace System::Drawing; using namespace System::Threading; //... Thread ^thisThread, ^thisThread2; private: void DoWork(int i) { button1->Text = Convert::ToString(i); } private: System::Void Thread_Start() { for (int i = 1; i <= 50; i++) { Invoke(gcnew Action<int>(this, &Form1::DoWork), i); Thread::Sleep(100); } } private: void DoWork2 (int i) { button2->Text = Convert::ToString(i); } private: System::Void Thread2_Start() { for (int i = 1; i <= 50; i++) { Invoke(gcnew Action<int>(this, &Form1::DoWork2), i); Thread::Sleep(100); } } private: System::Void button1_Click(System::Object^ sender, System::EventArgs^ e) { if (!thisThread || !thisThread->IsAlive) { thisThread = gcnew Thread(gcnew ThreadStart(this, &Form1::Thread_Start)); thisThread->Start(); } } private: System::Void button2_Click(System::Object^ sender, System::EventArgs^ e) { if (!thisThread2 || !thisThread2->IsAlive) { thisThread2 = gcnew Thread(gcnew ThreadStart(this, &Form1::Thread2_Start)); thisThread2->Start(); } } private: System::Void Form1_FormClosing(System::Object^ sender, System::Windows::Forms::FormClosingEventArgs^ e) { thisThread->Abort(); thisThread2->Abort(); }
3. Пример имеет чуть более сложный интерфейс и может останавливать (button1
) и запускать заново (button2
) потоки.
форма приложения 3
Один из потоков использует "бесконечный", точнее, прерываемый кнопкой "Стоп" цикл обработки - тот, что выводит текст
в нижний TextBox
, а в верхнем PictureBox
рисуются случайные пиксели самым простым кодом отсюда.
#pragma endregion private: System::Threading::Thread ^textThread, ^graphicsThread; private: int number; private: int sleepTime; private: volatile bool threadsState; private: Bitmap^ Img; private: Graphics^ g; private: System::Void MyForm_Load(System::Object^ sender, System::EventArgs^ e) { Img = gcnew Bitmap (pictureBox1->Size.Width, pictureBox1->Size.Height); g = Graphics::FromImage (Img); g->Clear(Color::White); pictureBox1->Image = Img; number = 1; //число в textBox sleepTime = 250; //интервал для приостановки потоков, мс threadsState = false; //активны ли потоки } private: System::Void textThreadDelegate() { while (threadsState) { textBox1->AppendText(number++ + System::Environment::NewLine); this->Text = "Id = "+System::Threading::Thread::CurrentThread->ManagedThreadId; Application::DoEvents(); //Обязательно в бесконечном цикле System::Threading::Thread::Sleep (sleepTime); } } private: System::Void textThreadMethod() { if (this->InvokeRequired) this->BeginInvoke(gcnew MethodInvoker(this, &MyForm::textThreadDelegate)); } private: System::Void imageThreadDelegate() { //while (threadsState) { int w = pictureBox1->Image->Width, h = pictureBox1->Image->Height; g->Clear(Color::White); for (int i = 0; i < w; i++) { for (int j = 0; j < h; j++) { int rc = 128 + rand() % 128; int gc = rand() % std::max(rc, 1); int bc = rand() % std::max(gc, 1); Img->SetPixel(i, j, Color::FromArgb(255, rc, gc, bc)); } } pictureBox1->Refresh(); this->Text = "Id = " + System::Threading::Thread::CurrentThread->ManagedThreadId; //} } private: System::Void imageThreadMethod() { if (this->InvokeRequired) this->BeginInvoke(gcnew MethodInvoker(this, &MyForm::imageThreadDelegate)); } private: System::Void button1_Click(System::Object^ sender, System::EventArgs^ e) { threadsState = true; textThread = gcnew System::Threading::Thread(gcnew System::Threading::ThreadStart(this, &MyForm::textThreadMethod)); textThread->IsBackground = true; graphicsThread = gcnew System::Threading::Thread(gcnew System::Threading::ThreadStart(this, &MyForm::imageThreadMethod)); graphicsThread->IsBackground = true; textThread->Start(); graphicsThread->Start(); button1->Enabled = false; //и отключить кнопку } private: System::Void button2_Click(System::Object^ sender, System::EventArgs^ e) { threadsState = false; textThread->Abort(); graphicsThread->Abort(); button1->Enabled = true; //и включить кнопку } private: System::Void MyForm_FormClosing(System::Object^ sender, System::Windows::Forms::FormClosingEventArgs^ e) { textThread->Abort(); graphicsThread->Abort(); }
Если раскомментировать цикл while (threadsState)
в методе imageThreadDelegate
- увы, нормально повторяться через интервал будет только один поток из двух.
Двух "бесконечных" циклов в потоках, к тому же, совместно работающих с потоком пользовательского интерфейса, следует, конечно, избегать, тем более, что в .NET есть просто таймеры.
Проект 3, файл .zip с проектом C++/CLI Visual Studio 2019, папка уже создана внутри архива (7 Кб)
18.11.2020, 00:52 [5338 просмотров]