- Windows Form потоки
- Новый поток в форме
- Решение
- Многопоточность в элементах управления Windows Forms Multithreading in Windows Forms Controls
- в этом разделе In This Section
- Справочник Reference
- Связанные разделы Related Sections
- С потоки windows form
- Аннотация
- Windows Forms и фоновая обработка
- Отмена длительной операции
- Взаимодействие через общие данные
- Взаимодействие через параметры методов
- Заключение
Windows Form потоки
Подскажите пожалуйста! Почему не создается второй поток?
Windows Form на VS C++
Есть стремление изучить WinForm на С++, но никакой подходящей конкретной литературы найти не.
Windows Form Application C++
Извиняюсь если задеваю тему которая уже ранее здесь была..но я увы ничего не нашел((:cry.
C++ windows form application
Как удалить созданный PictureBox? Заранее спасибо)
С++ Windows Form Application
У меня есть classname.cpp и classname.h куда правильно подключить класс чтобы с ним можно было.
В программе создать два потока. Назначение одного из них — периодическое чтение системного времени и заполнения глобальной структуры (часы, минуты, секунды), другой — вывод данной структуры на экран.
Заказываю контрольные, курсовые, дипломные и любые другие студенческие работы здесь или здесь.
Программа в Windows Form
Пишу программу с оконным интерфейсом,но у меня ругается на объект из другого класса,то есть в.
GetCursorPos() в windows form
Использую апишную ф-ию GetCursorPos() на что компилятор говорит 1>—— Построение начато.
Анимация Windows Form
Нужна помощь по заданию и реализации его в Visual Studio. Нужно изобразить действующий конвейер.
Windows Form и .cpp
Пишу в Visual Studio 2010 C++,возникла проблема .Создал я Windows Form и пытаюсь из своего файла.
Новый поток в форме
Invoke и новый поток
Пытаюсь запустить поток, в котором будут изменяться свойства listview. Но судя по зависаниям.
Запуск таймера через новый поток
вечер добрый помогите мне понять есть форма на ней таймер мне нужно именно с данного таймера не.
Передача в новый поток метода с параметрами
Люди нужен хелп: хочу создавать отдельные потоки для каждого клиента, но не получается их создать.
Создается ли новый поток на каждую форму?
Если у меня 10 форм, сколько потоков 10 или 1?
Решение
По идее, вычислительные операции должны выполнятся в одном потоке, а отображение в другом.
выше цитирование не то вставил ..
Заказываю контрольные, курсовые, дипломные и любые другие студенческие работы здесь или здесь.
Создать новый поток на добавление элементов в коллекцию
Доброго времени суток. Имеется приложение которое определяет является ли bitInteger из задаваемого.
Новый поток мешает открытию новой формы
У меня есть поток, после выполнения которого не открывается новая форма. Пробовал просто вызывать.
Для каждой вкладки с Gecko браузером — новый поток
Создаю приложение в котором есть 5 вкладок, в каждой вкладке Gecko браузер. using System; using.
AsyncCallback: создается ли новый поток на каждое принятое сообщение
Здравствуйте, не совсем понимаю так или не так: Принимаю на сервере сообщения от клиентов вот.
Многопоточность в элементах управления Windows Forms Multithreading in Windows Forms Controls
Во многих приложениях можно сделать пользовательский интерфейс более быстрым, выполняя длительные операции в другом потоке. In many applications, you can make your user interface (UI) more responsive by performing time-consuming operations on another thread. Существует ряд средств для многопоточности элементов управления Windows Forms, включая System.Threading пространство имен, Control.BeginInvoke метод и BackgroundWorker компонент. A number of tools are available for multithreading your Windows Forms controls, including the System.Threading namespace, the Control.BeginInvoke method, and the BackgroundWorker component.
BackgroundWorker Компонент заменяет и добавляет функции к System.Threading пространству имен и Control.BeginInvoke методу. Однако они сохраняются для обратной совместимости и использования в будущем, если вы решили. The BackgroundWorker component replaces and adds functionality to the System.Threading namespace and the Control.BeginInvoke method; however, these are retained for both backward compatibility and future use, if you choose. Дополнительные сведения см. в разделе Общие сведения о компоненте BackgroundWorker. For more information, see BackgroundWorker Component Overview.
в этом разделе In This Section
Практическое руководство. Осуществление потокобезопасных вызовов элементов управления Windows Forms How to: Make Thread-Safe Calls to Windows Forms Controls
Показывает, как выполнять потокобезопасные вызовы элементов управления Windows Forms. Shows how to make thread-safe calls to Windows Forms controls.
Практическое руководство. Применение фонового потока для поиска файлов How to: Use a Background Thread to Search for Files
Показывает, как использовать System.Threading пространство имен и BeginInvoke метод для асинхронного поиска файлов. Shows how to use the System.Threading namespace and the BeginInvoke method to search for files asynchronously.
Справочник Reference
BackgroundWorker
Документирует компонент, инкапсулирующий рабочий поток для асинхронных операций. Documents a component that encapsulates a worker thread for asynchronous operations.
LoadAsync
Документация по асинхронной загрузке звука. Documents how to load a sound asynchronously.
LoadAsync
Документация по асинхронной загрузке изображения. Documents how to load an image asynchronously.
Связанные разделы Related Sections
Практическое руководство. Фоновое выполнение операции How to: Run an Operation in the Background
Показывает, как выполнить трудоемкую операцию с BackgroundWorker компонентом. Shows how to perform a time-consuming operation with the BackgroundWorker component.
Общие сведения о компоненте BackgroundWorker BackgroundWorker Component Overview
Содержит разделы, в которых описывается использование BackgroundWorker компонента для асинхронных операций. Provides topics that describe how to use the BackgroundWorker component for asynchronous operations.
С потоки windows form
Обсудить на форуме
Еще раз о многопоточности в Windows Forms |
Рассматривается, как выполнять в рабочем потоке (worker thread) длительные операции, запускаемые из потока пользовательского интерфейса (UI). И при этом поддерживать связь с рабочим потоком, управляя его работой, что позволяет реализовать схему передачи сообщений между потоками для надежной и корректной обработки данных в нескольких потоках. |
Файл примера asynchcaclpi.exe можно скачать из MSDN Code Center. |
Аннотация
Рассматривается, как выполнять в рабочем потоке (worker thread) длительные операции, запускаемые из потока пользовательского интерфейса (UI). И при этом поддерживать связь с рабочим потоком, управляя его работой, что позволяет реализовать схему передачи сообщений между потоками для надежной и корректной обработки данных в нескольких потоках.
Как вы помните по одной из предыдущих статей, Safe, Simple Multithreading in Windows Forms, при соблюдении необходимых мер предосторожности можно весьма успешно разрабатывать многопоточные приложения Windows Forms. Многопоточность очень удобна, когда требуется выполнять длительные операции, например, вычислять большое количество разрядов числа pi (рис. 1).
Рис. 1. Приложение, вычисляющее заданное количество разрядов числа pi
Windows Forms и фоновая обработка
В прошлой статье мы сначала напрямую запускали поток для фоновой обработки, но потом перешли на запуск рабочего потока через асинхронные делегаты. У асинхронных делегатов удобный синтаксис для передачи аргументов, и они обеспечивают более высокую масштабируемость, так как при этом потоки берутся из пула, существующего в рамках процесса и управляемого общеязыковой исполняющей средой (CLR). Единственная серьезная проблема, с которой мы столкнулись, возникала, когда рабочему потоку нужно было уведомлять пользователя о ходе операции. Она заключалась в том, что нельзя напрямую работать с UI-элементами из рабочего потока (это давнее ограничение Win32 ® ). Из-за этого рабочий поток должен синхронно (send) или асинхронно (post) посылать сообщения UI-потоку с помощью методов Control.Invoke или Control.BeginInvoke, инициируя выполнение кода в потоке, которому принадлежат элементы управления. С учетом всех этих требований мы получили следующий код.
Обратите внимание, что используются два делегата. Первый, CalcPiDelegate, предназначен для «обертывания» аргументов, передаваемых методу CalcPi, который выполняется в рабочем потоке, выделяемом из пула потоков. Экземпляр этого делегата создается в обработчике события, когда пользователь запускает вычисление pi. Вызовом BeginInvoke операция ставится в очередь на выполнение в пуле потоков. Фактически первый делегат нужен, чтобы UI-поток передавал сообщения рабочему потоку.
Второй делегат, ShowProgressDelegate, предназначен для обратной передачи сообщений из рабочего потока в UI-поток, в том числе для обновления информации о ходе выполнения длительной операции. Чтобы оградить вызывающих от деталей безопасного в многопоточной среде взаимодействия с UI-потоком, метод ShowProgress отправляет в UI-поток сообщение для самого себя через Control.BeginInvoke и передает ему экземпляр ShowProgressDelegate. Control.BeginInvoke ставит операцию в очередь на асинхронное выполнение в UI-потоке и возвращает управление, не дожидаясь ее завершения.
Отмена длительной операции
В приведенном выше примере мы можем без всяких проблем передавать сообщения из UI-потока в рабочий поток и обратно. UI-потоку не приходится ждать, когда рабочий поток выполнит свою задачу и, более того, когда UI-поток получит уведомление о завершении рабочего потока, так как последний постоянно передает информацию о ходе своего выполнения. Аналогично рабочему потоку не приходится ждать, когда UI-поток покажет информацию о ходе выполнения, поскольку сообщения о ходе выполнения периодически посылаются UI-потоку, — так что пользователи счастливы. Но один момент пользователей вряд ли радует: у них нет полного контроля над обработкой, выполняемой приложением. Хотя UI остается «отзывчивым» во время вычисления числа pi, у пользователя по-прежнему нет возможности отменить вычисление, если он вдруг решит, что нужна была точность до 1 000 001 разрядов после запятой, а выбор 1 000 000 разрядов был ошибкой. На рис. 2 показан обновленный UI метода CalcPi, позволяющий отменять операцию.
Рис. 2. Пользователь может отменить длительную операцию
Реализация отмены длительной операции — процесс многоступенчатый. Прежде всего нужно предусмотреть в UI возможность отмены операции. В нашем случае после запуска вычисления название кнопки Calc меняется на Cancel. Еще одно распространенное решение — показывать диалоговое окно прогресса, в котором выводится информация о ходе выполнения, в частности индикатор прогресса, указывающий, на сколько процентов выполнена операция, и кнопка Cancel.
Если пользователь решил отменить операцию, этот факт нужно отразить в какой-то переменной-члене. Кроме того, необходимо отключить UI на короткий промежуток времени — с момента, когда UI-поток узнал, что нужно остановить рабочий поток, до момента, когда рабочий поток сам узнает об этом и сможет остановить передачу информации о ходе выполнения. Если не отключить UI на этот период, то пользователь может успеть запустить еще одну операцию перед тем, как рабочий поток прекратит отправлять информацию о ходе выполнения. Тогда UI-потоку придется выяснять, от какого рабочего потока приходит эта информация — от нового или от старого, считающегося прекратившим работу. Конечно, можно присвоить уникальный идентификатор каждому рабочему потоку, что позволит решить такую проблему. (Если требуется выполнять сразу несколько длительных операций, уникальная идентификация просто обязательна.) Но во многих случаях проще деактивировать UI на короткое время, о чем я уже говорил. Чтобы реализовать это в нашем несложном калькуляторе числа pi, используется перечислимое, состоящее из трех значений:
Теперь в зависимости от текущего состояния нажатие кнопки Calc обрабатывается по-разному:
Заметьте, что при нажатии кнопки Calc/Cancel в состоянии Pending, состояние меняется на Calculating (а также меняется надпись на кнопке), и, как и раньше, запускается асинхронное вычисление. Если при нажатии кнопки Calc/Cancel состояние было Calculating, оно меняется на Canceled, и UI отключается до тех пор, пока информация о том, что вычисление отменяется, не поступит в рабочий поток. Когда информация об отмене операции попадет в рабочий поток, мы снова активизируем UI и вернем состояние в Pending, разрешая пользователю запуск новой операции. Чтобы сообщить рабочему потоку о том, что он должен завершиться, добавим в метод ShowProgress out-параметр:
Может показаться соблазнительным сделать признак отмены логическим возвращаемым значением метода ShowProgress. Но лично мне было бы трудно запомнить, что означает true: что нужно отменить операцию или что все в порядке и можно продолжать выполнение операции. Чтобы код был более понятным, я решил использовать out-параметр.
Теперь остается лишь изменить метод ShowProgress, передающий данные между рабочим и UI-потоком. Нужно добиться, чтобы при запросе отмены операции ShowProgress реагировал на это и соответствующим образом уведомлял метод CalcPi. Как именно будет передаваться информация между UI и рабочим потоком, зависит от выбранного нами способа взаимодействия.
Взаимодействие через общие данные
Очевидный способ передать текущее состояние UI — позволить рабочему потоку напрямую изменять переменную-член _state. Казалось бы, для этого достаточно написать:
Надеюсь, при чтении этого кода вас немного покоробило (и не только из-за предупреждающего комментария). Если вы собираетесь программировать многопоточные приложения, то должны знать, что следует остерегаться любых ситуаций, при которых возможно одновременное обращение двух потоков к одним и тем же данным (в нашем случае, к _state). При совместном доступе к данным из разных потоков весьма вероятно, что возникнет так называемое состояние конкуренции (race condition): один поток читает данные, обновленные лишь частично, не дожидаясь, пока другой поток закончит их изменение. Чтобы параллельный доступ к данным выполнялся корректно, отслеживайте обращения к общим данным и обеспечивайте, чтобы каждый поток терпеливо ждал, пока другой поток завершит работу с этими данными. Для наблюдения за доступом к общим данным в .NET применяется класс Monitor, используемый как блокировка данных разделяемого объекта. В C# для этого класса имеется оболочка — удобный блок lock:
В этом фрагменте кода доступ к общим данным блокируется, но сделано это так, что весьма вероятна другая типичная проблема в многопоточной среде — взаимная блокировка (deadlock). При взаимной блокировке двух потоков каждый из них, чтобы продолжить выполнение, дожидается, пока другой поток завершит работу с данными, и в результате ни один поток не выполняется.
Если все, что здесь говорилось о состоянии конкуренции и взаимной блокировке, заставило вас задуматься, то это совсем неплохо. Управление параллельным доступом к общим данным со стороны нескольких потоков — крайне сложная задача. Пока что нам удавалось избегать этих проблем, так как мы передавали между потоками копии данных, которые каждый поток получал в единоличное владение. Если общих данных нет, то и синхронизация доступа к ним не нужна. Если вы обнаружите, что обойтись без общих данных нельзя (при копировании данных слишком велики издержки, связанные с расходованием памяти или времени), то придется изучить принципы разделения данных между несколькими потоками (см. справочные материалы по этой тематике).
Однако в подавляющем большинстве ситуаций, особенно при использовании многопоточности в работе с UI, пожалуй, лучше применять простую схему передачи сообщений, как мы до сих пор и делали. Обычно не требуется, чтобы UI имел доступ к данным, обрабатываемым в фоне (например, к печатаемому документу или к набору перечисляемых объектов). В таких случаях лучше всего избегать совместного использования данных.
Взаимодействие через параметры методов
Мы уже добавили out-параметр в метод ShowProgress. Ничто не мешает проверять в методе ShowProgress значение переменной _state, когда метод выполняется в UI-потоке:
Так как к переменной-члену _state обращается только UI-поток, синхронизация не нужна. А значит, достаточно передать управление UI-потоку, так чтобы получить значение out-параметра cancel делегата ShowProgressDelegate. К сожалению, при использовании Control.BeginInvoke возникают сложности. Проблема в том, что BeginInvoke не будет дожидаться, пока вызов ShowProgress в UI-потоке возвратит результаты, поэтому у нас есть два варианта. Первый — в методе ShowProgress, выполняемом в UI-потоке, перед возвратом управления вызвать BeginInvoke, передав ему еще один делегат. Но этот делегат будет выполняться в другом потоке из пула, так что снова возникает проблема синхронизации, на сей раз между рабочим потоком и другим потоком из пула. Второй вариант проще — использовать синхронный метод Control.Invoke и дожидаться возврата out-параметра cancel. Однако, как вы увидите из следующего кода, даже в этом случае есть тонкости:
Для получения параметра cancel было бы идеально сразу передать переменную типа Boolean методу Control.Invoke, но тут возникает проблема: bool — это тип значения (value type), тогда как Invoke принимает в качестве параметра массив объектов, а объекты относятся к ссылочным типам (reference types). В справочных материалах (см. ниже) перечислены книги, где рассматриваются различия между этими типами. Суть в том, что при передаче переменной типа bool как объекта ее значение копируется, а реальное значение остается неизменным. Из-за этого мы никогда не узнаем об отмене операции. Чтобы избежать такой ситуации, приходится создавать объект (inoutCancel) и передавать его вместо bool — тогда копирования не происходит. После синхронного вызова Invoke переменная типа object приводится к типу bool, и можно определить, отменена ли операция.
Различия между ссылочными типами и типами значений следует учитывать при вызове Control.Invoke (или Control.BeginInvoke) с out или ref-параметрами, относящимся к типам значений, например, примитивным int и bool или более сложным перечислимым и структурам. Но если передаются более сложные данные, такие как ссылочный тип, созданный разработчиком (класс), то для правильной работы ничего особенного не потребуется. Однако, на мой взгляд, даже неудобство работы с типами значений при вызове Invoke/BeginInvoke — приемлемая плата за возможность избавиться от параллельного доступа к общим данным и писать код, не подверженный взаимным блокировкам и состоянию конкуренции.
Заключение
Итак, изучая, казалось бы, тривиальный пример, мы рассмотрели некоторые весьма сложные вопросы. Мы не только воспользовались многопоточностью, чтобы избавить UI от выполнения длительной операции, но и обеспечили передачу пользовательского ввода в рабочий поток, благодаря чему пользователь может управлять выполнением рабочего потока. При этом мы могли бы использовать общие данные, но, чтобы избежать проблем синхронизации (которые всегда возникают как раз в тот момент, когда начальник хочет посмотреть, как работает ваш код), предпочли применить схему передачи сообщений. В результате получен надежный, корректно работающий код многопоточной обработки.