Часть 1: Потоки
Не следует использовать new Thread
:
- Потоки стоят дорого в Microsoft Windows (в отличие от Linux, где потоки обычно дешевы ).
- Используйте пул потоков, который содержит потоки, которые уже созданы. NET и ожидают работы.
- Использование
Task.Run
запустит задание в пуле потоков поток по умолчанию и представляет задание как Task
объект, который вы можете безопасно await
, потому что диспетчер WPF поддерживает планировщик Task
и всегда будет возобновлять работу в потоке пользовательского интерфейса после await
. - Обратите внимание, что даже если это означает использование ключевых слов
async
и await
, этот код на самом деле не является «настоящим» асинхронным кодом (в отличие от реального asyn c IO для сетевых сокетов и файловой системы) - это просто. NET представляет одновременно выполняющийся фоновый поток как асинхронную операцию. - При использовании
await
в контексте WPF (или WinForms) важно, чтобы никогда звоните .ConfigureAwait(false)
, если вы не теперь безопасно возобновить работу в потоке без пользовательского интерфейса. (Но можно использовать .ConfigureAwait(true)
).
Это также делает ваш код на намного проще . Вот все, что вам нужно:
private async void Run_Clicked( Object sender, EventArgs e )
{
User compare = new User( this.Parser1, this.Parser2 );
busyIndicator.IsBusy = true;
await Task.Run( () => compare.compare() );
busyIndicator.IsBusy = false;
MessageBoxResult result = Xceed.Wpf.Toolkit.MessageBox.Show( "Finished", "Generation Finished", MessageBoxButton.OK );
}
Тем не менее, поскольку await
перезапустит любые исключения, вы всегда должны восстанавливать состояние формы внутри блока finally
:
private async void Run_Clicked( Object sender, EventArgs e )
{
User compare = new User( this.Parser1, this.Parser2 );
busyIndicator.IsBusy = true;
try
{
await Task.Run( () => compare.compare() );
MessageBoxResult result = Xceed.Wpf.Toolkit.MessageBox.Show( "Finished", "Generation Finished", MessageBoxButton.OK );
}
finally
{
busyIndicator.IsBusy = false;
}
}
Часть 2. Отчет о ходе выполнения
Чтобы указать информацию о ходе выполнения для вызывающего абонента, используйте IProgress<T>
. Аргументом типа IProgress<T>
должно быть неизменяемое значение или объект (т. Е. Тип значения, например int
или float
, неизменяемый ссылочный тип, например string
, ValueTuple
или пользовательский объект класса с только readonly
полями).
- Я предполагаю, что вы хотите сообщить только процентное значение, поэтому мы будем используйте
float
(он же Single
) для T
(со значениями 0-1.0
, представляющими 0-100%
. - . Я не рекомендую использовать
int
для представления процентов, поскольку это означает " разрешение "индикатора выполнения будет слишком низким (всего 100 шагов), а не плавно прогрессирует от 0 до 100% с десятичными знаками, например, 0,1%, 55,55%, 99,95% и т. д. c. - Если вы хотите вернуть
String
сообщение с процентом, затем используйте ValueTuple
: IProgress<(String message, float percentage)>
.
Сначала измените метод compare
(который должен называться Compare
, кстати) принять IProgress<float>
и сообщить о прогрессе, используя progress?.Report( value )
(используйте оператор ?.
, чтобы разрешить вызывающим абонентам отказаться от предоставления IProgress
объекта): * 108 4 *
class User
{
public void Compare( IProgress<float> progress = null )
{
for( Int32 i = 0; i < 100; i++ )
{
progress?.Report( i / 100f );
}
progress?.Report( 1f ); // 100% completion
}
}
В вашем коде WPF мы будем использовать реализацию по умолчанию IProgress<T>
in. NET, называемую Progress<T>
, которая обеспечивает встроенную синхронизацию потока пользовательского интерфейса :
Любой обработчик, предоставленный конструктору или обработчикам событий, зарегистрированным в событии ProgressChanged, вызывается через экземпляр SynchronizationContext, захваченный при его создании.
Примерно так:
class MyWpfWindow
{
private void UpdateProgressBar( float value )
{
this.progressBar.Value = value * 100; // Assuming ProgressBar's scale is 0-100.
}
private async void Run_Clicked( Object sender, EventArgs e )
{
User compare = new User( this.Parser1, this.Parser2 );
busyIndicator.IsBusy = true;
Progress<float> progress = new Progress<float>( this.UpdateProgressBar );
try
{
await Task.Run( () => compare.Compare( progress ) );
MessageBoxResult result = Xceed.Wpf.Toolkit.MessageBox.Show( "Finished", "Generation Finished", MessageBoxButton.OK );
}
finally
{
busyIndicator.IsBusy = false;
}
}
}