Dispatcher Invoke (…) против BeginInvoke (…) путаницы

Я смущен, почему я не могу заставить это приложение счетчика тестов работать с 2 (или более) одновременными запусками countertextboxes с использованием «BeginInvoke» в моем диспетчере в методе Count ().

Вы можете решить проблему, заменив BeginInvoke на Invoke. Но это не решает мою путаницу.

Вот пример кода, о котором я говорю:

public class CounterTextBox : TextBox { private int _number; public void Start() { (new Action(Count)).BeginInvoke(null, null); } private void Count() { while (true) { if (_number++ > 10000) _number = 0; this.Dispatcher.BeginInvoke(new Action(UpdateText), System.Windows.Threading.DispatcherPriority.Background, null); } } private void UpdateText() { this.Text = "" + _number; } } 

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

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

Фактическое действие, которое вы используете, использует поле _number . Но _number изменяется другим streamом очень быстро и пока действие находится в очереди . Это означает, что он не отображает значение _number в момент, когда вы запланировали действие, а скорее то, что оно происходит после того, как оно продолжается в очень узком цикле.

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

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

Затем вам нужно взять копию _number перед передачей ее Dispatcher чтобы она отображала значение в то время, когда вы его запланировали, а не во время его выполнения:

 while (true) { if (_number++ > 10000) _number = 0; int copy = _number; this.Dispatcher.BeginInvoke(new Action(() => UpdateText(copy)) , System.Windows.Threading.DispatcherPriority.Background, null); Thread.Sleep(200); } 

 private void UpdateText(int number) { this.Text = number.ToString(); } 
Interesting Posts
Давайте будем гением компьютера.