I was under the impression that using Task's ContinueWith method with a UI context allows performing operations on UI elements without causing a cross-thread exception. However, I am still getting an exception when running the following code:
var context = TaskScheduler.FromCurrentSynchronizationContext();
Task<SomeResultClass>.Factory.StartNew(SomeWorkMethod).ContinueWith((t) =>
{
myListControl.Add(t.Result); // <-- this causes an exception
}, context);
Any ideas?
There are two different causes of cross-thread exceptions.
The most common one is that you try to modify the state of a control from the non-UI thread. And this is not the issue you're hitting.
The one you're hitting is that controls must be created on the UI thread. Your task is creating the control on a different thread and when you try to add this control to controls created on the UI thread you get the exception.
You need to separate out the work from the control creation to make this work. Try returning a Func<Control>
rather than a Control
and invoke this on the UI thread before adding it. Keep most of the work on the task thread but form a nice tight closure by returning the Func<>
that just does the control creation.
Aside from the reasons and possible solutions Enigmativity told allready you can allways do something like this:
var context = TaskScheduler.FromCurrentSynchronizationContext();
Task<SomeResultClass>.Factory.StartNew(SomeWorkMethod).ContinueWith((t) =>
{
if (!myListControl.InvokeRequired)
myListControl.Add(t.Result); // <-- this causes an exception
else
myListControl.Invoke((Action)(() => myListControl.Add(t.Result)));
}, context);
(assuming this is WinForms)
if you want mor control refactor the add into a method and use the InvokeRequired inside the method to call itself inside the Invoke if needed:
private void AddToListControl(MyItem item)
{
if (myListControl.InvokeRequired)
{
myListControl.Invoke((Action)(() => AddToListControl(item)));
return;
}
myListControl.Add(item);
}
What Enigmativity was hinting at is something like this:
var result =
Task<Action>.Factory.StartNew(SomeWorkMethod).ContinueWith((t) =>
{
return () => myListControl.Add(t.Result);
});
result.Result();
But IMHO this is just the same place you got from the start because you have to call the Result-Action on the right thread yet again.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With