Интервью Вопрос: Когда Control.InvokeRequired вы используете Control.Invoke или Control.BeginInvoke? - PullRequest
6 голосов
/ 24 октября 2010

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

, если элементу управления потребуется 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 ]

15 голосов
/ 24 октября 2010

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

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

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

5 голосов
/ 24 октября 2010

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

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

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

0 голосов
/ 24 октября 2010

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

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

...