Что не так с вызовом Invoke, независимо от InvokeRequired?
Я видел общие настройки для многопоточного доступа к элементу управления с графическим интерфейсом, такие как обсужденные здесь: Кратчайший способ написать потокобезопасный метод доступа к элементу управления Windows формы
Все найденные мной веб-хиты описывают похожую вещь.
Однако зачем нам проверять InvokeRequired? Разве мы не можем просто позвонить в Invoke напрямую?
Я предполагаю, что ответ - нет, поэтому мой реальный вопрос - "почему"?
6 ответов
Из потоков, не связанных с пользовательским интерфейсом, мы не можем касаться пользовательского интерфейса - могут случиться очень плохие вещи, поскольку элементы управления имеют сходство с потоками. Так что из потока, не являющегося пользовательским интерфейсом, мы должны (как минимум) вызвать Invoke
или же BeginInvoke
,
Для UI-потоков, однако - мы не хотим звонить Invoke
много времени; проблема в том, что если вы уже находитесь в потоке пользовательского интерфейса, у него все еще есть ненужные накладные расходы, связанные с отправкой сообщения в насос формы и его обработкой.
На самом деле, в большинстве многопоточных программ вы знаете, что ожидаете вызова определенного метода в потоке, не являющемся пользовательским интерфейсом, поэтому в этих случаях нет дополнительных затрат: просто вызовите Invoke
,
Если вы попытаетесь вызвать до создания дескриптора окна (например, при вызове конструктора формы), вы получите InvalidOperationException
, Итак, в общем InvokeRequired
проверка обязательна.
Посмотрите MSDN для деталей.
InvokeRequired
в основном говорит вам, выполняете ли вы в нужном потоке или нет. Если вы находитесь не в нужном потоке, вам нужно перенаправить задачу в правильный поток, в противном случае это не так. Отсюда и необходимость проверки.
Проблема заключается в том, что для элементов управления GUI есть требование, чтобы только код, выполняющийся в том же потоке, который использовался для создания экземпляра элемента управления GUI, мог получить доступ к элементу управления GUI. Причины этого требования связаны с тем, как Windows спроектирована. Достаточно сказать, что это было бы очень трудно изменить.
InvokeRequired проверяет идентичность текущего исполняющего потока и идентичность создаваемого потока. Если они одинаковы, код может свободно взаимодействовать с элементом управления. Если нет, код должен перенаправить данные из текущего потока в поток создания экземпляра. Это медленный и дорогостоящий процесс, и его следует избегать, если это вообще возможно. Ваш код будет работать, если вы всегда вызываете, и может случиться так, что вы не заметите снижения производительности, но этот сценарий будет становиться все более распространенным по мере использования многоядерных систем. Лучше не создавать кодовые "узлы", которые должны быть отменены позже.
Invoke собирается вызывать код через делегат, а не напрямую, что будет дорогостоящим.
Экономически выгодно вызывать Invoke только тогда, когда это требуется. Следовательно, InvokeRequired используется для выяснения, выполняется ли вызов из того же потока или другого потока?
Одна из причин, по которой я могу думать, - это производительность. Если в большинстве случаев вызывающий поток совпадает с потоком-создателем, у вас могут возникнуть некоторые несекретные издержки.