threading: always call delegate.EndInvoke?
-
Hi audience! on several places ther is demanded in a very unconditional manner: "when starting a thread with
aDelegate.BeginInvoke()
always call the correspondingaDelegate.EndInvoke(IAsyncResult ar)
.
For example see MSDN itself [ ^ ] .
But now i look in Reflector, how theBackgroundworker
-class works, and i find:public void RunWorkerAsync(object argument)
{
if (this.isRunning)
{
throw new InvalidOperationException(SR.GetString("BackgroundWorker_WorkerAlreadyRunning"));
}
this.isRunning = true;
this.cancellationPending = false;
this.asyncOperation = AsyncOperationManager.CreateOperation(null);
this.threadStart.BeginInvoke(argument, null, null);
}And
this.threadStart
is simply a private class-variable of typeWorkerThreadStartDelegate
, which is defined as:private delegate void WorkerThreadStartDelegate(object argument);
Since
this.threadStart.BeginInvoke(argument, null, null);
does not save the returned
IAsyncResult
, nor passes anAsyncCallback
to theBeginInvoke()
-call, i see no option for the backgroundworker, to callthis.threadStart.EndInvoke(IAsyncResult ar)
propperly.So i think to myself: if the parallel processed method is
void
, actually there is no need to callaDelegate.EndInvoke(IAsyncResult ar)
?
I would be pleased of that, because that could simplyfy some of my codes.So can anyone confirm my conclusion?
-
Hi audience! on several places ther is demanded in a very unconditional manner: "when starting a thread with
aDelegate.BeginInvoke()
always call the correspondingaDelegate.EndInvoke(IAsyncResult ar)
.
For example see MSDN itself [ ^ ] .
But now i look in Reflector, how theBackgroundworker
-class works, and i find:public void RunWorkerAsync(object argument)
{
if (this.isRunning)
{
throw new InvalidOperationException(SR.GetString("BackgroundWorker_WorkerAlreadyRunning"));
}
this.isRunning = true;
this.cancellationPending = false;
this.asyncOperation = AsyncOperationManager.CreateOperation(null);
this.threadStart.BeginInvoke(argument, null, null);
}And
this.threadStart
is simply a private class-variable of typeWorkerThreadStartDelegate
, which is defined as:private delegate void WorkerThreadStartDelegate(object argument);
Since
this.threadStart.BeginInvoke(argument, null, null);
does not save the returned
IAsyncResult
, nor passes anAsyncCallback
to theBeginInvoke()
-call, i see no option for the backgroundworker, to callthis.threadStart.EndInvoke(IAsyncResult ar)
propperly.So i think to myself: if the parallel processed method is
void
, actually there is no need to callaDelegate.EndInvoke(IAsyncResult ar)
?
I would be pleased of that, because that could simplyfy some of my codes.So can anyone confirm my conclusion?
I think you've been reading MSDN far too closely and I should state at the outset that I don't have an answer to the question. The confusion around this issue must arise out of inconsistencies in the Microsoft documentation. For example, there is a disparity between your reference and the information in Event Based Asynchronous Pattern[^]. The first gives the impression that the world will end if the EndInvoke call is omitted, yet the second does just that and does not explain why we're still here. I haven't found an example that demonstrates a problem caused by not calling EndInvoke or been able to come up with one of my own. My own best idea to make something go wrong was this:
internal void InvokeTest(Int32 count) { IAsyncResult ar; for (Int32 i = 0; i < count; i++) { TaskDelegate t = Task; ar = t.BeginInvoke(null, null); ar.AsyncWaitHandle.WaitOne(); } } private void Task() { // create a big exception object throw new Exception('a', 64 \* 1024); }
I thought that the ignored exceptions on the invoked thread might be retained by the CLR until EndInvoke was called but find that there is no gradual upwards drift in memory useage (Process Explorer). If the Exception objects were not being collected it should be very obvious given the size of the associated exception message. To err on the side of caution one should include EndInvoke but I can't find any compelling evidence one way or the other. So no help from me then, but I thought your post was looking lonely! Alan.
-
I think you've been reading MSDN far too closely and I should state at the outset that I don't have an answer to the question. The confusion around this issue must arise out of inconsistencies in the Microsoft documentation. For example, there is a disparity between your reference and the information in Event Based Asynchronous Pattern[^]. The first gives the impression that the world will end if the EndInvoke call is omitted, yet the second does just that and does not explain why we're still here. I haven't found an example that demonstrates a problem caused by not calling EndInvoke or been able to come up with one of my own. My own best idea to make something go wrong was this:
internal void InvokeTest(Int32 count) { IAsyncResult ar; for (Int32 i = 0; i < count; i++) { TaskDelegate t = Task; ar = t.BeginInvoke(null, null); ar.AsyncWaitHandle.WaitOne(); } } private void Task() { // create a big exception object throw new Exception('a', 64 \* 1024); }
I thought that the ignored exceptions on the invoked thread might be retained by the CLR until EndInvoke was called but find that there is no gradual upwards drift in memory useage (Process Explorer). If the Exception objects were not being collected it should be very obvious given the size of the associated exception message. To err on the side of caution one should include EndInvoke but I can't find any compelling evidence one way or the other. So no help from me then, but I thought your post was looking lonely! Alan.
Thank you very much! Especially
Alan N wrote:
...but I thought your post was looking lonely! ...
shows a noble character, and i'm happy to see, such magnanimity still exists in the real world :) . Finally i found an answer, and a reasonable reason to call
Action<whatever>.EndInvoke()
: Exceptions of the sideThread are saved to the calling thread (May be you knew that already, to me it's new). In Designmode there is no difference - the Debugger stops where the exception is thrown. But in Release-mode it's important, and very dangerous, because a lost exception leaves the program running in an instable State!using System.Windows.Forms;
using System;
using System.Threading;namespace AsyncWorkerCs {
public partial class frmMain : Form {\[STAThread\] static void Main() { Application.EnableVisualStyles(); Application.Run(new frmMain()); } public frmMain() { InitializeComponent(); btCreateError.Click += btCreateError\_Click; } void btCreateError\_Click(object sender, EventArgs e) { var createError = new Action(CreateError); // in Release-mode this will crash the app (recommended behavior if an unhandled exception occurs). //createError.BeginInvoke(createError.EndInvoke, null); // in Release-mode this will cause no Exception, but leave the app running in an undefined state! createError.BeginInvoke(null, null); } void CreateError() { Thread.Sleep(500); throw new Exception("CreateError()!"); // in Debug-mode IDE always will stop here }
}
}Hmm, now i've found a reason to call Action.EndInvoke, it's one more reason to deprecate the Backgroundworker-class, isn't it? a very strong reason to deprecate it, since the dangerous behavior does not occur in Debug-mode. So it causes its surprises really late. regards