Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Многопоточность.docx
Скачиваний:
0
Добавлен:
01.04.2025
Размер:
113.32 Кб
Скачать

Взаимодествие с пользовательским интерфейсом

Последним вопросом, который нам необходимо обсудить, является взаимодействие отдельного потока с пользовательским интерфейсом. На самом деле графический интерфейс пользователя (GUI) может исполняться только в том же потоке, в котором был создан. О том, как этого добиться, и рассказывает эта часть статьи.

WinForms

Несмотря на стремительное наступление технологии WPF, все еще много приложений пишется на WinForms. Начнем же наше рассмотрение взаимодействия потоков с пользовательским интерфейсом в WinForms-приложениях (проект WindowForms).

Метод Invoke

Наиболее распространенным способом выполнить свой код гарантировано в основном потоке является использование метода Invoke класса Control (форма Invoke). Этот метод гарантированно исполняет код переданного ему делегата в том потоке, в котором был создан соответствующий элемент Control:

Func<long> dlgt = new Func<long>(GetCheckNumber);

return (long) this.Invoke( dlgt );

Просто оберните вызов вашего метода в делегат и передайте его методу Invoke. Кроме того, класс Control имеет свойство InvokeRequired, позволяющий узнать, нужно ли вызывать для вашего коде Invoke, или можно выполнять его напрямую. В связи с этим полный код метода, который должен выполняться в потоке пользовательского интерфейса, выглядит следующим образом:

private long GetCheckNumber()

{

if (this.InvokeRequired)

{

Func<long> dlgt = new Func<long>(GetCheckNumber);

return (long) this.Invoke( dlgt );

}

else

{

return long.Parse(tbNumber.Text);

}

}

Следует отметить, что вызов таких методов из вашего рабочего потока не должен быть слишком частым, поскольку в этом случае окажется, что вы постоянно нагружаете поток пользовательского интерфейса и у него не остается времени на свои задачи (например, на отрисовку). Это будет эквивалентно выполнению всего вашего кода в одном потоке, чего мы хотели избежать. Кроме того, слишком частый вызов Invoke замедляет выполнение вашего потока.

Использование SynchronizationContext

Как я уже говорил, метод Invoke является наиболее распространенным способом синхронизации пользовательского интерфейса с отдельным потоком. Однако у него есть недостаток, иногда ограничивающий его применимость. Дело в том, что для его работы элемент, у которого он вызывается, должен быть отображен на экране. Говоря более строго, у него должен быть действительный (рабочий, валидный) handle окна. Т.е. элемент не только должен быть создан конструктором, но и отображен. Это не всегда возможно. В данном случае на помощь приходит класс SynchronizationContext (форма SynchronizationContext). Получить объект этого класса очень просто. Для этого используется его статическое свойство Current. Оно возвращает синхронизационный контекст потока. Однако знайте, что у потока может не быть синхронизационного контекста. Поэтому это свойство может возвращать и null. Однако метод Application.Run , запускающий WinForms–приложение, всегда устанавливает этот контекст для потока пользовательского интерфейса. Поэтому вы можете безопасно получить его, обычно это делается по событию загрузки главной формы:

private void MainForm_Load(object sender, EventArgs e)

{

m_SyncContext = SynchronizationContext.Current;

}

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

Синхронизационный контекст имеет 2 основных метода: Send и Post. Обоим передается делегат SendOrPostCallback и параметр для него. Первый выполняет переданный ему делегат синхронно (блокируя вызвавший метод Send поток до тех пор, пока делегат не будет исполнен), а второй – асинхронно.

Применение этих методов может выглядеть примерно таким образом:

m_SyncContext.Send(

new SendOrPostCallback(

delegate

{

pb.Minimum = 0;

pb.Maximum = 100;

pb.Value = 0;

btnCheck.Enabled = false;

}

),

null

);

Использование анонимных методов позволяет избавиться от необходимости писать отдельные методы и передавать им параметры.