If an object is created and has events assigned in one thread and is then ran from a different thread on which thread does the event run?
If I create an object in the main UI thread and then call a method within that object from a different thread will any events raised run in the seperate thread or the main UI thread?
Example:
WebClient client = new WebClient();
client.DownloadData开发者_StackOverflowCompleted += new DownloadDataCompletedEventHandler(
delegate(object sender, DownloadDataCompletedEventArgs e)
{
Thread.Sleep(60000);
});
BackgroundWorker worker = new BackgroundWorker();
worker.DoWork += new DoWorkEventHandler(
delegate(object sender, DoWorkEventArgs e)
{
Thread.Sleep(60000);
client.DownloadDataAsync("http://www.example.com");
});
worker.RunWorkerAsync();
Would hooking the events up to their own methods instead of delegates make any difference?
Thanks.
The event is raised synchronously on the thread that invoked it, that is, all subscribers to the event are run in the thread that raised the event.
You can also raise an event asynchronosly via BeginInvoke, in this case I believe it is handled 'eventually' by a thread from the application thread pool.
You need to safe guard any handlers that interact with any UI components against execution on a separate thread from the main UI thread.
You can do this via Control.InvokeRequired/Control.Invoke(...) technique, See
Event handlers are run on the thread that invokes them, like any other method. However, if the handler is defined on a class that implements ISynchronizeInvoke
(such as winforms controls), you can invoke it on the thread that created it. Here's an extension method I use for raising events that automatically handles this:
/// <summary>
/// Fires an event and catches any exceptions raised by an event handler.
/// </summary>
/// <param name="ev">The event handler to raise</param>
/// <param name="sender">The sender of the event.</param>
/// <param name="e">Event arguments for the event.</param>
/// <typeparam name="T">The type of the event args.</typeparam>
public static void Fire<T>(this EventHandler<T> ev, object sender, T e) where T : EventArgs
{
if (ev == null)
{
return;
}
foreach (Delegate del in ev.GetInvocationList())
{
try
{
ISynchronizeInvoke invoke = del.Target as ISynchronizeInvoke;
if (invoke != null && invoke.InvokeRequired)
{
invoke.Invoke(del, new[] { sender, e });
}
else
{
del.DynamicInvoke(sender, e);
}
}
catch (TargetInvocationException ex)
{
ex.InnerException.PreserveStackTrace();
throw ex.InnerException;
}
}
}
/// <summary>
/// Called on a <see cref="TargetInvocationException"/> to preserve the stack trace that generated the inner exception.
/// </summary>
/// <param name="e">The exception to preserve the stack trace of.</param>
public static void PreserveStackTrace(this Exception e)
{
var ctx = new StreamingContext(StreamingContextStates.CrossAppDomain);
var mgr = new ObjectManager(null, ctx);
var si = new SerializationInfo(e.GetType(), new FormatterConverter());
e.GetObjectData(si, ctx);
mgr.RegisterObject(e, 1, si);
mgr.DoFixups();
}
EDIT: the PreserveStackTrace thing isn't part of the answer to this question really, it's just part of the solution I use. I actually got that method from another answer on SO, I can't remember exactly where from though, but credit for it does belong to someone else. Sorry I can't remember who.
精彩评论