многопоточность c windows form

пошаговое руководство. осуществление потокобезопасных вызовов элементов управления Windows Forms

многопоточность может повысить производительность Windows Forms приложений, но доступ к элементам управления Windows Forms не является потокобезопасным. Многопоточность может представлять код для очень серьезных и сложных ошибок. Два или более потока, управляющих элементом управления, могут привести к нестабильному состоянию и вызвать условия гонки, взаимоблокировки, зависания или фиксации. При реализации многопоточности в приложении следует обязательно вызывать элементы управления между потоками потокобезопасным образом. Дополнительные сведения см. в разделе рекомендации по управляемому потоку.

Ненадежные вызовы между потоками

Вызвать элемент управления напрямую из потока, который не создал его, неважно. В следующем фрагменте кода показан незащищенный вызов System.Windows.Forms.TextBox элемента управления. Button1_Click Обработчик событий создает новый WriteTextUnsafe поток, который устанавливает свойство основного потока TextBox.Text напрямую.

Сейф вызовов между потоками

в следующих примерах кода демонстрируются два способа безопасного вызова элемента управления Windows Forms из потока, который не создал его.

В обоих примерах фоновый поток заждет одну секунду для имитации работы, выполняемой в этом потоке.

Пример. использование метода Invoke с делегатом

в следующем примере показан шаблон для обеспечения потокобезопасных вызовов элемента управления Windows Forms. Он запрашивает System.Windows.Forms.Control.InvokeRequired свойство, которое СРАВНИВАЕТ идентификатор потока создаваемого элемента управления с идентификатором вызывающего потока. Если идентификаторы потоков совпадают, он вызывает элемент управления напрямую. Если идентификаторы потоков отличаются, он вызывает Control.Invoke метод с делегатом из основного потока, который выполняет фактический вызов элемента управления.

Пример. использование обработчика событий BackgroundWorker

Простой способ реализации многопоточности заключается в использовании System.ComponentModel.BackgroundWorker компонента, использующего модель, управляемую событиями. Фоновый поток запускает BackgroundWorker.DoWork событие, которое не взаимодействует с основным потоком. Главный поток запускает BackgroundWorker.ProgressChanged BackgroundWorker.RunWorkerCompleted обработчики событий и, которые могут вызывать элементы управления основного потока.

Источник

Простая и безопасная реализация многопоточности в Windows Forms. Часть 1


Автор: Крис Селлз (Chris Sells)
Sells Brothers Consulting
Источник: GotDotNet.ru

Опубликовано: 05.06.2003
Исправлено: 13.03.2005
Версия текста: 1.0

Введение

многопоточность c windows form
Рис. 1. Приложение Digits of Pi

Индикация хода выполнения длительных операций

многопоточность c windows form
Рис. 2. Вычисление pi с точностью до 1000 знаков

Ниже приведен код, обновляющий пользовательский интерфейс (UI) по мере вычисления знаков pi.

Все шло замечательно, пока в середине вычисления pi с точностью до 1000 знаков я не переключился в другое приложение, а потом вернулся обратно. То, что я увидел, показано на рис. 3.

многопоточность c windows form
Рис. 3. Событие paint пропало!

Асинхронные операции

На тот момент обработчик события Click выглядел так:

Не забудьте, проблема в том, что до тех пор, пока CalcPi не вернет управление, поток не выйдет из обработчика Click, а значит, форма не сможет обрабатывать событие Paint (или любое другое). Решить эту проблему можно, например, запустив другой поток:

многопоточность c windows form
Рис. 4. Примитивная многопоточность

Теперь, когда у меня есть делегат, я могу создать экземпляр, синхронно вызывающий функцию CalcPi:

Теперь я должен был бы быть доволен. В моем приложении полностью интерактивный UI сообщал о ходе выполнения длительных вычислений. И я был доволен, пока не понял, что натворил.

Безопасная многопоточность

Первая перегруженная версия Invoke принимает экземпляр делегата, содержащего метод, который нужно вызвать в UI-потоке. Никаких аргументов она не предполагает. Однако функция, вызываемая для обновления UI (ShowProgress), принимает три аргумента, поэтому нам потребуется вторая перегруженная версия. Чтобы аргументы передавались корректно, нам понадобится еще один делегат для метода ShowProgress. Применение метода Invoke гарантирует, что вызовы ShowProgress и обращения к окну будут происходить в корректном потоке (не забудьте заменить оба вызова ShowProgress в CalcPi):

Метод Invoke наконец-то позволил мне безопасно использовать многопоточность в приложении Windows Forms. UI-поток порождает рабочий, который выполняет длительную операцию и возвращает управление UI-потоку, когда возникает необходимость в обновлении пользовательского интерфейса. Безопасная многопоточная архитектура показана на рис. 5.

многопоточность c windows form
Рис. 5. Безопасная многопоточность

Упрощенная многопоточность

BeginInvoke всегда предпочтительнее, если возвращаемое назначение не требуется, поскольку при использовании этого метода рабочий поток сразу же возвращается к своей работе, что исключает вероятность взаимной блокировки.

Заключение

В этом коротком примере я показал, как выполнять длительные операции так, чтобы можно было отображать ход их выполнения, а UI продолжал реагировать на действия пользователя. С этой целью я использовал асинхронный делегат для запуска рабочего потока и метод Invoke для главной формы. При этом еще один делегат выполнялся в UI-потоке.

Конечно, если вы имеете дело с большими наборами данных, вы вряд ли захотите их копировать. Однако везде, где это возможно, я советую для реализации длительных операций в приложениях Windows Forms сочетать асинхронные делегаты и передачу сообщений между рабочим и UI-потоками.

Благодарности

Источник

Многопоточность в C#: как работать с потоками в C# и как в потоке обращаться к элементам формы

Как создатель данной программы для сбора специфической технической информации я знаю, что приложение работает, но вот клиенты нашей компании сообщают технической поддержке, что наше приложение при работе постоянно зависает. Поэтому решил я исправить данную ситуацию и погрузился в изучение потоков. Написано много о блокировке, совместном доступе потоков, обращение к элементам формы и т.д. У начинающего от этого начинает мозг плавиться из-за большого и не совсем понятного потока информации.

Как работать с потоками в C#

После изучения данной темы, проб и ошибок, упрощения и написания кода всё оказалось достаточно просто. Сейчас постараюсь простыми словами объяснить, как запустить задачу в отдельном потоке.

Далее у нас есть некая функция 1 (func1) в которой выполняется некий код и в середине этого кода у нас есть кусок кода, который мы хотим выполнить в отдельном потоке. Для этого нам необходимо написать дополнительную функция 2 (func2), в которую вынести весь код, который мы будем запускать в отдельном потоке. После этого в нужном месте функции 1 нам надо вызвать отдельный поток и продолжить выполнение кода, в то время, когда в отдельном потоке параллельно будет выполняться другой код. Теперь всё вышесказанное мы опишем кодом.

Функции привёл в обратном порядке, чтобы далее прокомментировать функцию 1. Сначала у нас выполняется некий «КОД 1» после этого мы запускаем первый поток и сразу продолжаем выполнять «КОД 2», потом доходим до запуска второго потока и его запускаем и сразу переходим к выполнению следующего кода «КОД 3». В итоге у нас два потока выполняют код из функций «func2» и «func3» одновременно с выполнением кода основной функции «func1».

В данном примере у нас после выполнения «КОД 3» происходит выход из функции 1 при этом потоки продолжают свою работу пока не выполнится весь вызываемый ими код. Есть способ, который позволяет при завершении главной функции, которая запустила потоки, сразу завершать и выполнение потоков, для этого добавляем потоку параметр «IsBackground = true»:

Теперь у нас поток будет завершён сразу после выполнения «КОД 2».

Если вы хотите узнать в каком состоянии сейчас находится запущенный вами поток, то вам необходимо получить его статус «potok1.ThreadState». Подробное описание статусов можете просмотреть на сайте Microsoft.

Как в отдельном потоке в C# обращаться к элементам формы

Если вы запустили отдельный поток, то вы не сможете из этого потока обращаться к элементам формы напрямую, так как вам будет выдаваться ошибка мол вы обращаетесь к элементам, которые были созданы в другом потоке. Эта ситуация поправима, и мы сейчас рассмотрим решение.

Обратите внимание, где он объявляется.

Теперь создадим функцию, в которой будем изменять требуемый нам элемент:

Теперь в коде отдельного потока мы с вами изменим элемент обратившись к функции изменяющий требуемый элемент:

А что делать если нам надо изменить элемент передав в него какое-то значение? Для этого объявляем «MyDelegate» с переменной:

И создаём функцию, в которой будем изменять требуемый нам элемент, с такой же переменной.

Теперь в коде отдельного потока изменим элемент обратившись к функции изменяющий требуемый элемент и передав требуемое нами значение:

Основа заложена, а дальше уже остаётся самостоятельно постигать глубинный смысл потоков 🙂

Источник

How to: Make thread-safe calls to Windows Forms controls

Multithreading can improve the performance of Windows Forms apps, but access to Windows Forms controls isn’t inherently thread-safe. Multithreading can expose your code to very serious and complex bugs. Two or more threads manipulating a control can force the control into an inconsistent state and lead to race conditions, deadlocks, and freezes or hangs. If you implement multithreading in your app, be sure to call cross-thread controls in a thread-safe way. For more information, see Managed threading best practices.

There are two ways to safely call a Windows Forms control from a thread that didn’t create that control. You can use the System.Windows.Forms.Control.Invoke method to call a delegate created in the main thread, which in turn calls the control. Or, you can implement a System.ComponentModel.BackgroundWorker, which uses an event-driven model to separate work done in the background thread from reporting on the results.

Unsafe cross-thread calls

It’s unsafe to call a control directly from a thread that didn’t create it. The following code snippet illustrates an unsafe call to the System.Windows.Forms.TextBox control. The Button1_Click event handler creates a new WriteTextUnsafe thread, which sets the main thread’s TextBox.Text property directly.

Safe cross-thread calls

The following code examples demonstrate two ways to safely call a Windows Forms control from a thread that didn’t create it:

In both examples, the background thread sleeps for one second to simulate work being done in that thread.

Example: Use the Invoke method with a delegate

The following example demonstrates a pattern for ensuring thread-safe calls to a Windows Forms control. It queries the System.Windows.Forms.Control.InvokeRequired property, which compares the control’s creating thread ID to the calling thread ID. If the thread IDs are the same, it calls the control directly. If the thread IDs are different, it calls the Control.Invoke method with a delegate from the main thread, which makes the actual call to the control.

Example: Use a BackgroundWorker event handler

An easy way to implement multithreading is with the System.ComponentModel.BackgroundWorker component, which uses an event-driven model. The background thread runs the BackgroundWorker.DoWork event, which doesn’t interact with the main thread. The main thread runs the BackgroundWorker.ProgressChanged and BackgroundWorker.RunWorkerCompleted event handlers, which can call the main thread’s controls.

To make a thread-safe call by using BackgroundWorker, create a method in the background thread to do the work, and bind it to the DoWork event. Create another method in the main thread to report the results of the background work, and bind it to the ProgressChanged or RunWorkerCompleted event. To start the background thread, call BackgroundWorker.RunWorkerAsync.

The example uses the RunWorkerCompleted event handler to set the TextBox control’s Text property. For an example using the ProgressChanged event, see BackgroundWorker.

Источник

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *