Ответ 1
Разница между Invoke и BeginInvoke заключается в том, что первая является синхронной (ждет завершения), а позже - асинхронной (вроде fire-and-forget). Тем не менее, обе работают, отправляя сообщение в цикл сообщений пользовательского интерфейса, что приведет к тому, что делегат будет выполнен, когда он дойдет до этого сообщения.
Свойство InvokeRequired определяет, нужно ли вообще вызывать Invoke или если он уже находится в правильном потоке, а не хотите ли вы синхронного или асинхронного вызова. Если InvokeRequired является ложным, вы (теоретически) уже работаете в потоке пользовательского интерфейса и можете просто выполнять синхронные действия напрямую (или все же BeginInvoke, если вам нужно асинхронно запускать их). Это также означает, что вы не можете использовать Invoke, если InvokeRequired имеет значение false, потому что нет пути для продолжения цикла сообщений в текущем потоке. Так что одна большая проблема с вашим кодом выше, но не обязательно ошибка, о которой вы сообщаете. Фактически вы можете использовать BeginInvoke в любом случае, если вы следите за рекурсивным вызовом и так далее.
Однако вы не можете использовать ни один из них без дескриптора окна. Если Form/Control был создан, но не инициализирован (т.е. До его первого показа), он может еще не иметь дескриптора. И ручка очищается Dispose(), например, после закрытия формы. В любом случае InvokeRequired вернет false, потому что невозможно вызвать без дескриптора. Вы можете проверить IsDisposed, и есть также свойство IsHandleCreated, которое более конкретно проверяет, существует ли дескриптор. Обычно, если IsDisposed является истинным (или IsSandleCreated является ложным), вы хотите использовать инструмент для особого случая, например, просто отказаться от действия как неприменимого.
Таким образом, код, который вы хотите, скорее всего, похож:
if (IsHandleCreated)
{
// Always asynchronous, even on the UI thread already. (Don't let it loop back here!)
BeginInvoke(new UpdateTextFieldDelegate(WriteToForm), finished, numCount);
return; // Fired-off asynchronously; let the current thread continue.
// WriteToForm will be called on the UI thread at some point in the near future.
}
else
{
// Handle the error case, or do nothing.
}
Или, может быть:
if (IsHandleCreated)
{
// Always synchronous. (But you must watch out for cross-threading deadlocks!)
if (InvokeRequired)
Invoke(new UpdateTextFieldDelegate(WriteToForm), finished, numCount);
else
WriteToForm(finished, numCount); // Call the method (or delegate) directly.
// Execution continues from here only once WriteToForm has completed and returned.
}
else
{
// Handle the error case, or do nothing.
}