Интервью Вопрос: Когда Control.InvokeRequired вы используете Control.Invoke или Control.BeginInvoke?

У меня недавно было одно из тех действительно плохих интервью, где они играют с тобой хорошего полицейского / плохого полицейского. Что бы я ни ответил, этого недостаточно для одного из них, и моя уверенность уменьшалась с каждой минутой. Его последний вопрос, который действительно смутил меня, был следующим:

если элемент управления будет нуждаться в InvokeRequired, будет ли разница в выполнении.Invoke или.BeginInvoke?

Позвольте мне показать вам пример, как я понимаю это:

public delegate string WorkLongDelegate(int i);

var del = new WorkLongDelegate(WorkLong);
var callback = new AsyncCallback(CallBack);
del.BeginInvoke(3000, callback, del);

public string WorkLong(int i)
{
      Thread.Sleep(i);
      return (string.Format("Work was done within {0} seconds.", i));            
}

private void CallBack(IAsyncResult ar)
{
    var del = (WorkLongDelegate) ar.AsyncState;
    SetText2(del.EndInvoke(ar));
}

private void SetText2(string s)
{
   if(InvokeRequired)
   {
       // What is the difference between BeginInvoke and Invoke in below?
       BeginInvoke(new MethodInvoker(() => textBox1.Text = s)); 
   }
   else
   {
       textBox1.Text = s;
   }
}

Я упоминал, что BeginInvoke будет делать это асинхронно, в то время как Invoke будет останавливать поток пользовательского интерфейса до его выполнения. Но этого было недостаточно. Тем не менее, я не понимаю значения производительности здесь, если бы я использовал Invoke вместо этого. Может кто-нибудь, пожалуйста, просветите меня?

3 ответа

Решение

Invoke не останавливает поток пользовательского интерфейса. Он блокирует продолжение вызывающего потока, пока поток пользовательского интерфейса не завершится.

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

С другой стороны, если вам нужно извлечь что-то из потока пользовательского интерфейса (что довольно редко, по общему признанию), то вы можете использовать Invoke вместо. Я бы сказал, что вы должны использовать BeginInvoke если у вас нет конкретной причины использовать Invoke, Но в любом случае, вы должны понимать разницу:)

Очень очевидный вариант использования Invoke() - это когда вам нужно вызвать метод, возвращаемое значение которого вам нужно. Только Invoke() может обеспечить это для вас, он возвращает Object, метод, возвращающий значение.

Более слабый - это когда ваш рабочий поток выдает результаты со скоростью, намного превышающей скорость пользовательского интерфейса. Использование Invoke() душит работника, и список вызовов не может расти без ограничений. Это, однако, всего лишь бинты для решения более серьезной проблемы, нет смысла обновлять пользовательский интерфейс быстрее, чем может воспринять человек. Один раз каждые 40 миллисекунд выглядит гладко для человеческого глаза. Вы по-прежнему хотите использовать Invoke(), если потоку пользовательского интерфейса требуется слишком много времени для обработки коллекции результатов. Классическими признаками возникновения проблемы, подобной этой, является зависание потока пользовательского интерфейса, отсутствие возможности рисовать и реагировать на события мыши и клавиатуры, потому что оно полностью перегружено запросами вызова. И поток пользовательского интерфейса оставался без ответа некоторое время после того, как рабочий завершил работу, занятый работой с отставанием.

Другой случай - требования к блокировке объектов, которые вы передаете в BeginInvoke(). Блокировка не требуется, если вы используете Invoke(), поток пользовательского интерфейса и рабочий поток не могут получить доступ к объекту одновременно. Это не относится к BeginInvoke, он продолжает работать, и если поток продолжает использовать один и тот же объект, вы должны защитить объект с помощью блокировки как в потоке пользовательского интерфейса, так и в рабочем. Если эта блокировка мешает работнику делать какие-либо успехи, вы можете также использовать Invoke(). Все это довольно необычно, так как основной поток начинает выполнять делегат очень долго. И всегда полезно создать новый экземпляр объекта после его вызова, чтобы не было необходимости в блокировке.

Естественно, если вы используете версию asnyc, ваш фоновый поток может продолжаться немедленно, не дожидаясь переключения контекста.

Обычно это должно быть быстрее (для фонового потока), что я понимаю.

Другие вопросы по тегам