Вам не нужно добавлять блокировку. Запросы Dispatcher.Invoke и BeginInvoke не будут выполняться в середине другого кода (в этом их суть).
Просто две вещи для рассмотрения:
- BeginInvoke может быть более подходящим в этом случае, Invoke поставит в очередь запрос и затем заблокирует вызывающий поток, пока поток пользовательского интерфейса не станет свободным и не завершит выполнение кода, BeginInvoke будет только ставить запрос в очередь без блокировки.
- Некоторые операции, особенно операции, которые открывают окна (в том числе окна сообщений) или осуществляют межпроцессное взаимодействие, могут разрешить выполнение операций диспетчера очереди.
РЕДАКТИРОВАТЬ: во-первых, у меня нет ссылок, потому что на страницах MSDN по этому вопросу, к сожалению, очень мало деталей, - но я написал тестовые программы для проверки поведения BeginInvoke, и все, что я пишу здесь, является результатом этих тестов .
Теперь, чтобы перейти ко второму пункту, нам сначала нужно понять, что делает диспетчер. Очевидно, это очень упрощенное объяснение.
Любой пользовательский интерфейс Windows работает путем обработки сообщений; Например, когда пользователь наводит указатель мыши на окно, система отправит этому окну сообщение WM_MOUSEMOVE.
Система отправляет сообщение, добавляя его в очередь, каждый поток может иметь очередь, все окна, созданные одним и тем же потоком, совместно используют одну и ту же очередь.
В основе каждой программы Windows есть цикл, называемый «цикл сообщений» или «насос сообщений», этот цикл читает следующее сообщение из очереди и вызывает код соответствующего окна для обработки этого сообщения.
В WPF этот цикл и вся связанная с ним обработка обрабатываются Диспетчером.
Приложение может либо находиться в цикле сообщений, ожидая следующего сообщения, либо оно может что-то делать. Вот почему при длительном вычислении все окна потока перестают отвечать - поток занят работой и не возвращается в цикл обработки сообщений для обработки следующего сообщения.
Dispatcher.Invoke и BeginInvoke работают, ставя запрошенную операцию в очередь и выполняя ее в следующий раз, когда поток возвращается в цикл сообщений.
Именно поэтому Dispatcher. (Begin) Invoke не может «внедрить» код в середину вашего метода, вы не вернетесь к циклу сообщений, пока ваш метод не вернется.
НО
Любой код может запустить цикл сообщений. Когда вы вызываете все, что выполняет цикл обработки сообщений, будет вызван Dispatcher, и он может выполнять операции (Begin) Invoke.
Какой код имеет цикл сообщений?
- Все, что имеет графический интерфейс или принимает пользовательский ввод, например, диалоговые окна, окна сообщений, перетаскивание и т. Д. - если бы в них не было цикла сообщений, приложение не отвечало бы и не могло обрабатывать пользовательский ввод.
- Межпроцессное взаимодействие, которое использует сообщения Windows за сценой (большинство методов межпроцессного взаимодействия, включая COM, используют их).
- Все остальное, что занимает много времени и не останавливает систему (то, что система не зависает, является доказательством того, что она обрабатывает сообщения).
Итак, подведем итог:
- Диспетчер не может просто вставить код в ваш поток, он может выполнять код только тогда, когда приложение находится в «цикле сообщений».
- Любой написанный вами код не имеет циклов сообщений, если вы не написали их явно.
- Большая часть кода пользовательского интерфейса не имеет собственного цикла сообщений, например, если вы вызываете Window.Show, а затем выполняете некоторые длительные вычисления, окно появится только после того, как вычисление завершено, и метод возвращается (и приложение возвращается к цикл обработки сообщений и обрабатывает все сообщения, необходимые для открытия и рисования окна).
- Но любой код, который взаимодействует с пользователем до его возврата (MessageBox.Show, Window.ShowDialog), должен иметь цикл обработки сообщений.
- Некоторый код связи (сетевой и межпроцессный) использует циклы сообщений, а некоторые нет, в зависимости от конкретной используемой вами реализации.