Понимание диспетчера WPF.BeginInvoke
У меня создалось впечатление, что dispatcher
будет следовать приоритету
операций, поставленных в очередь, и выполнения операций на основе приоритета
или порядок, в котором операция была добавлена в очередь (если такой же приоритет)
пока мне не сказали, что это не так в случае WPF UI dispatcher
.
Мне сказали, что если операция в потоке пользовательского интерфейса занимает больше времени, скажем, что чтение базы данных
диспетчер UI просто пытается выполнить следующий набор операций в очереди.
Я не мог согласиться с этим, поэтому решил написать образец приложения WPF, который содержит кнопку и три прямоугольника, при нажатии кнопки прямоугольники заполняются разными цветами.
<StackPanel>
<Button x:Name="FillColors" Width="100" Height="100"
Content="Fill Colors" Click="OnFillColorsClick"/>
<TextBlock Width="100" Text="{Binding Order}"/>
<Rectangle x:Name="RectangleOne" Margin="5" Width="100" Height="100" Fill="{Binding BrushOne}" />
<Rectangle x:Name="RectangleTwo" Margin="5" Width="100" Height="100" Fill="{Binding BrushTwo}"/>
<Rectangle x:Name="RectangleThree" Margin="5" Width="100" Height="100" Fill="{Binding BrushThree}"/>
</StackPanel>
и в коде
private void OnFillColorsClick(object sender, RoutedEventArgs e)
{
var dispatcher = Application.Current.MainWindow.Dispatcher;
dispatcher.BeginInvoke(new Action(() =>
{
//dispatcher.BeginInvoke(new Action(SetBrushOneColor), (DispatcherPriority)4);
//dispatcher.BeginInvoke(new Action(SetBrushTwoColor), (DispatcherPriority)5);
//dispatcher.BeginInvoke(new Action(SetBrushThreeColor), (DispatcherPriority)6);
dispatcher.BeginInvoke(new Action(SetBrushOneColor));
dispatcher.BeginInvoke(new Action(SetBrushTwoColor));
dispatcher.BeginInvoke(new Action(SetBrushThreeColor));
}), (DispatcherPriority)10);
}
private void SetBrushOneColor()
{
Thread.Sleep(10 * 1000);
Order = "One";
//MessageBox.Show("One");
BrushOne = Brushes.Red;
}
private void SetBrushTwoColor()
{
Thread.Sleep(12 * 1000);
Order = "Two";
//MessageBox.Show("Two");
BrushTwo = Brushes.Green;
}
private void SetBrushThreeColor()
{
Thread.Sleep(15 * 1000);
Order = "Three";
//MessageBox.Show("Three");
BrushThree = Brushes.Blue;
}
public string Order
{
get { return _order; }
set
{
_order += string.Format("{0}, ", value);
RaisePropertyChanged("Order");
}
}
Прокомментированный код работает так, как ожидалось, методы вызываются на основе DispatcherPriority
, и я также могу увидеть обновление экрана после завершения каждой операции.
Order
One, Two, Three
. Цвета рисуются один за другим.
Теперь рабочий код, где DispatcherPriority
не указан
(Я предполагаю, что он по умолчанию равен Normal
), порядок все еще One, Two, Three
, но если я покажу a MessageBox
внутри методов, всплывающее окно Thrid
появится сначала, затем Two
, затем One
, но когда я отлаживаю, я мог видеть, что методы запускаются в ожидаемом порядке (IntelliTrace даже показывает, что отображается окно сообщения, но я не вижу его на экране в то время и вижу его только после завершения последней операции.) его просто, что MessageBox
es показаны в обратном порядке.
Это потому, что MessageBox.Show
является блокирующим вызовом, и операция очищается после того, как сообщение было закрыто.
Даже тогда порядок MessageBox
также должен быть One
, Two and
Three`
Ответы
Ответ 1
Прежде чем перейти к вашему поведению кода, это является необходимым условием для понимания приоритетов Dispatcher
. DispatcherPriority
делится на диапазоны, как показано на рисунке ниже.
![DispatcherPriority]()
Если вы просто ставите 4 действия на 4 выше диапазона на Dispatcher
. очередь Foreground
будет выполнена сначала, затем Background
и затем в последней очереди Idle
. приоритет 0 не будет выполнен.
Теперь ваш код:
Три задачи ставятся в очередь 1-й в Background
, 2-й в Background
и 3-й в Foreground
очереди. Итак, третий будет выполнен в первую очередь. то вторая задача вызывает более высокий приоритет, чем 1-я задача. Я надеюсь, что это очистит.
Хотя некоторые дополнительные наблюдения помогут вам лучше понять, что, если вы задали приоритеты как 7,8 и 9. Так как это очередь переднего плана, 7 будет выполняться сначала, а затем 7, а затем 8. Один на один и исключительно в этом порядке, а пока 7 выполняется, 8 и 9 будут ждать, то есть очередь Foreground
будет выполняться синхронно друг с другом.
Но Background
и Idle
очередь не будет вести себя таким образом, когда выполнение асинхронно с другими задачами и задачами будет следовать приоритету. И сначала Background
, и Idle
очередь.
Надеемся, что это объяснение в какой-то мере поясняется.
Ответ 2
Это связано с тем, что первый MessageBox
блокирует поток UI.
Что Dispatcher.BeginInvoke()
делает под капотом, принимает ваш делегат и планирует его запустить в основном потоке пользовательского интерфейса во время следующего периода ожидания. Однако MessageBox
будет блокировать любую нить, из которой она вызывается, до ее закрытия. Это означает, что второй MessageBox
не может отображаться до тех пор, пока первый не будет очищен, так как планировщик потоков пользовательского интерфейса видит, что поток уже используется (ожидание первого MessageBox
будет очищено) и не может выполнить следующий делегат, содержащий второй MessageBox
.