Calling method from DoWork handler? - c#

I am attempting to use a BackgroundWorker to complete a task. I have got the worker to run correctly, under the DoWork method it then calls another method which executes but then I face my problem: when that method tries to call another method it does not succeed and does not throw an exception and I can only see this as being something I am doing wrong with BackgroundWorker since when running on the UI thread for testing the methods execute as intended.
Here is where I run my worker:
private void btnAddShots_Click(object sender, EventArgs e)
{
backgroundWorker.RunWorkerAsync();
}
Here is my DoWork method:
private void backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
int noOfShots = dataGridShots.Rows.Count - 1;
int count = 0;
while (count < noOfShots)
{
addTaskPair(dataGridShots.Rows[count].Cells[0].Value.ToString(),
dataGridShots.Rows[count].Cells[1].Value.ToString(),
dataGridShots.Rows[count].Cells[2].Value.ToString());
count += 1;
}
}
Here is a stripped down version of addTaskPair method that is called by my worker:
private void addTaskPair(string taskName, string taskDescription, string taskPriority)
{
try
{
Task trackingTask = new Task();
trackingTask.content = taskName;
trackingTask.description = taskDescription;
trackingTask.priority = taskPriority;
string trackingJson = JsonConvert.SerializeObject(trackingTask);
trackingJson = "{ \"todo-item\":" + trackingJson + " }";
string jsonResponse;
jsonResponse = postJSON(trackingJson, teamworkURL + "/tasklists/"
+ todoLists.todoLists[cmbTrackingList.SelectedIndex].id + "/tasks.json");
}
catch (Exception e)
{
debugMessage(e.ToString());
}
}
Within the above sample you will see that I call the method postJSON, this is where I hit my wall. Through testing I have verified that the above method runs, but the postJSON method does not run at all when being called from within this thread.
I have seen many references to Invoking when researching this problem but they all seem to apply to changing ui controls which I do not need to do (though a progress bar is manipulated using the ProgressChanged BackgroundWorker event).
I can clarify my problem more if needs be but I am really hoping for assistance with this since I have never successfully worked with backgroundworker or threading before (I am not a professional as I'm sure you can tell from my code).

You're working with the UI controls from the BackgroundWorker.DoWork event handler. Don't do that.
Collect the data before starting the BackgroundWorker, and pass it as an argument to the RunWorkerAsync method. Do not touch the UI from the BackgroundWorker.DoWork - progress updates through the ReportProgress method are fine.
Also, if you're running on .NET 4.5+, you might want to consider using the new Task pattern instead. It still requires you to collect the data to process beforehand, but it's a lot easier to work with:
(EDIT: As Peter suggested, the invalid access is happening in cmbTrackingList.SelectedIndex; I've included it in the code below. That's exactly the reason I suggested using static methods for the operations happening in a separate thread - it makes you think a lot more about the data you're working with)
var todoList = todoLists.todoLists[cmbTrackingList.SelectedIndex];
var data =
dataGridShots
.Rows
.Select
(
i =>
new
{
TaskName = i.Cells[0].Value.ToString(),
TaskDescription = i.Cells[1].Value.ToString(),
TaskPriority = i.Cells[2].Value.ToString()
}
)
.ToArray();
var result =
await Task.Run
(
() =>
foreach (var row in data)
handleRowData(row.TaskName, row.TaskDescription, row.TaskPriority, todoList)
);
Now that you're this far, you might notice that it shouldn't be too hard to make your postJson method asynchronous as well (there's plenty of ways to make HTTP requests asynchronously) - this will allow you to make your whole code asynchronous without blocking any thread.
Multi-threading is hard. Always try to work with the highest possible abstraction, and avoid any shared state. If you do need shared state, you need to synchronize every single access to it, from each of the threads - try to avoid that (a good practice being having the methods executing on different threads static, so that you don't accidentaly touch shared state).

From your description, it seems that the access of the DataGrid components is not the problem. That is, it seems those statements get executed correctly, and the addTaskPair() method is successfully called, but the postJSON() method is not.
Given that, I suspect that the evaluation of cmbTrackingList.SelectedIndex is what's throwing an exception and interrupting the thread.
That said, the advice is still the same: keep the UI-related stuff on the UI thread, and only run the other stuff outside the UI thread. Given the code you've posted, it seems likely that the only thing that really ought to be asynchronous (i.e. run in the background, so as to not delay the UI thread too much) is the call to postJSON(). Presumably this is a synchronous network call, and so could take awhile. The other stuff should run deterministically and quickly.
Given that, here's how I'd refactor the code, taking advantage of the new async/await feature:
private async void btnAddShots_Click(object sender, EventArgs e)
{
int noOfShots = dataGridShots.Rows.Count - 1;
int count = 0;
while (count < noOfShots)
{
await addTaskPair(dataGridShots.Rows[count].Cells[0].Value.ToString(),
dataGridShots.Rows[count].Cells[1].Value.ToString(),
dataGridShots.Rows[count].Cells[2].Value.ToString());
count += 1;
}
}
private async Task addTaskPair(string taskName, string taskDescription, string taskPriority)
{
try
{
TaskData trackingTask = new TaskData();
trackingTask.content = taskName;
trackingTask.description = taskDescription;
trackingTask.priority = taskPriority;
string trackingJson = JsonConvert.SerializeObject(trackingTask);
trackingJson = "{ \"todo-item\":" + trackingJson + " }";
string jsonResponse;
string url = teamworkURL + "/tasklists/"
+ todoLists.todoLists[cmbTrackingList.SelectedIndex].id + "/tasks.json";
jsonResponse = await Task.Run(() => postJSON(trackingJson, url));
}
catch (Exception e)
{
debugMessage(e.ToString());
}
}
NOTE: in the above I changed the name of your own Task type to TaskData. I strongly recommend you pick a name other than Task, due to the pervasive use of the .NET Task type throughout the modern .NET API.
In the above, most of the code will run on the UI thread. The async methods are rewritten by the compiler to return at any await statement, and to resume execution of that method when the awaited Task has completed. Note that the async method only returns when there is finally a Task object to await; so in the above, the btnAddShots_Click() method will initially return once the addTaskPair() method has called Task.Run() and itself has returned at the await statement.
Important: in this context, calling and awaiting an asynchronous method from the UI thread causes the framework to run the rest of the method back on the UI thread. That is, when the asynchronous operation has completed, control of the execution of the code is returned back to the UI thread where you started.
It's this feature that makes all of this work correctly, so it's useful to make sure you understand it. :)
The call to postJSON() is executed in a separate thread, using the Task object that is created by the Task.Run() method. Since that will be executing on other than the UI thread, I've moved the computation of its URL argument to a local variable just before the call to Task.Run(), which variable is then passed to the postJSON() method when it's called in the task thread. Doing this ensures that the evaluation of cmbTrackingList.SelectedIndex is done in the UI thread.
EDIT:
Noting that the OP has commented he is using .NET 4 and not 4.5 (in which the async/await feature was officially released), I offer this slightly more awkward alternative, which still preserves the execution characteristics of the preferable 4.5-compatible version above. While one can install the async/await features on VS2010 (and IMHO that's a better way to go), this alternative allows for "pure" .NET 4 code while still achieving basically the same runtime result.
private void btnAddShots_Click(object sender, EventArgs e)
{
Action<Task> continuation = null;
TaskScheduler uiScheduler = TaskScheduler.FromCurrentSynchronizationContext();
int noOfShots = dataGridShots.Rows.Count - 1;
int count = 0;
// Note that the continuation delegate is chained, attaching itself as
// the continuation for each successive task, thus achieving a looping
// mechanism.
continuation = task =>
{
if (count < noOfShots)
{
addTaskPair(dataGridShots.Rows[count].Cells[0].Value.ToString(),
dataGridShots.Rows[count].Cells[1].Value.ToString(),
dataGridShots.Rows[count].Cells[2].Value.ToString())
.ContinueWith(continuation, uiScheduler);
count += 1;
}
}
// Invoking the continuation delegate directly gets the ball rolling
continuation(null);
}
private Task addTaskPair(string taskName, string taskDescription, string taskPriority)
{
try
{
TaskData trackingTask = new TaskData();
trackingTask.content = taskName;
trackingTask.description = taskDescription;
trackingTask.priority = taskPriority;
string trackingJson = JsonConvert.SerializeObject(trackingTask);
trackingJson = "{ \"todo-item\":" + trackingJson + " }";
string url = teamworkURL + "/tasklists/"
+ todoLists.todoLists[cmbTrackingList.SelectedIndex].id + "/tasks.json";
// NOTE: must explicitly specify TaskScheduler.Default, because
// the default scheduler in the context of a Task is whatever the
// current scheduler is, which while executing a continuation would
// be the UI scheduler, not TaskScheduler.Default.
return Task.Factory.StartNew(() => postJSON(trackingJson, url),
CancellationToken.None, TaskCreationOptions.None, TaskScheduler.Default)
.ContinueWith(task =>
{
if (task.Exception != null)
{
// Task exceptions are wrapped in an AggregateException
debugMessage(task.Exception.InnerException.ToString());
}
else
{
string jsonResponse = task.Result;
// do something with jsonResponse?
}
}, TaskScheduler.FromCurrentSynchronizationContext());
}
catch (Exception e)
{
debugMessage(e.ToString());
}
}
Notes:
The task scheduler for the current synchronization context is used for each continuation. This ensures that the continuation itself is executed back on the UI thread, where you can safely interact with UI objects.
The most awkward part of this IMHO is that your while loop becomes an if statement, since the execution of the loop spans multiple method invocations.
While async/await would normally allow normal exception-handling syntax, when using explicit continuations you don't have this option. But Task will wrap any exceptions that occur in an AggregateException instance, which you can use to get at the real exception and report it.
TaskFactory.StartNew() has a somewhat subtle behavior: the scheduler it uses to run the given task is TaskScheduler.Current. The first time the addTaskPair() method is called, there is no current task, and so TaskScheduler.Current returns the default (i.e. thread pool) scheduler. But every subsequent time the addTaskPair() method is called, it is from within a task continuation, and so TaskScheduler.Current would return the scheduler being used to execute the continuation. Of course, we've intentionally made this the UI scheduler and running the new postJSON() task using that scheduler would defeat the purpose, as it would just execute synchronously on the current thread. So it's imperative here to specify the scheduler we want, i.e. TaskScheduler.Default, corresponding to the thread pool scheduler.
Without async/await, it's a bit harder to get things just right. It's syntactically more verbose, but IMHO it's still a reasonably good option, as it substantially preserves the desired, imperative structure of the code. In particular, you get to keep the flow of execution in the UI thread, making access of UI objects trivial, and forking off the long-running operation(s) only as needed.
(I should also point out that this .NET 4 version is not strictly exactly what the compiler would generate for you when using async/await. It's very similar, but not quite the same. Also, I will point out that while implementing a method with a single await in this way is not too bad, it gets a bit out of hand if you want multiple continuations in the same method. It's possible, but at that point I'd think the urge to just upgrade to the latest version of C# would be very compelling :) ).
Finally…
If after all of the above, you want to stick with BackgroundWorker, it should be possible to avoid the exception that's occurring with a relatively simple change to your code:
int selectedIndex = (int)Invoke((Func<int>)(() => cmbTrackingList.SelectedIndex));
jsonResponse = postJSON(trackingJson, teamworkURL + "/tasklists/"
+ todoLists.todoLists[selectedIndex].id + "/tasks.json");
I.e. just use the Control.Invoke() method to invoke on the UI thread an anonymous method that will return the value of cmbTrackingList.SelectedIndex. The Control.Invoke() method will receive the returned value, and will in turn return it to you. Since Control.Invoke() is general-purpose, its object return type must be cast to the type you know is being returned.
This ensures that the cmbTrackingList object is accessed only on the UI thread. I'll also note that if this index is not expected to change while the background processing is going on (or is specifically not supposed to), then yet another alternative would be to retrieve the value in your btnAddShots_Click() method and then pass it down to the DoWork event handler, which in turn would pass it to the addTaskPair() method where it's needed.
I put this option last because I really believe that learning the async/await feature is important and worthwhile, and that while the BackgroundWorker class has served us well over the years, it's essentially been deprecated by the new features. But I also readily admit that BackgroundWorker is still a fine way to do things, and can be made to work in your scenario.

Related

Start async method in new thread - No overload

I have these methods in a class
public async Task GetCompanies(int requestDuration, long startTimepoint)
{
_requestDuration = requestDuration;
_startTimepoint = startTimepoint;
Thread thread = new Thread(new ThreadStart(Test));
// This line doesnt compile - No overload for GetCompaniesApi matches delegate ThreadStart
Thread thread2 = new Thread(new ThreadStart(GetCompaniesApi));
}
public void Test()
{
}
public async Task GetCompaniesApi (int requestDuration, long? startTimepoint)
{
// code removed as not relevant
}
So my question is how can I run a method that is async in a different thread, I don't really know what "No overload for GetCompaniesApi matches delegate ThreadStart" means, or what I need to change.
EDIT
If I just explain fully what i'm trying to do that might be better than the more specific question I asked at the start.
Basically I want to call a HTTP GET request which is streaming, as in it never ends, so I want to force the HTTP GET request to end after X seconds and whatever we have got from the response body at that point will be it.
So in order to try and do this I thought i'd run that HTTP GET request in a separate thread, then sleep the main thread, then somehow get the other thread to stop. I don't see how its possible to use cancellation tokens as the thread is stuck on line "await _streamToReadFrom.CopyToAsync(streamToWriteTo);" all the time.
public async Task GetCompanies(int requestDuration, long startTimepoint)
{
Task task = Task.Run(() => { GetCompaniesApi(requestDuration, startTimepoint); });
Thread.Sleep(requestDuration * 1000);
// Is it now possible to cancel task?
}
public async Task GetCompaniesApi (int requestDuration, long? startTimepoint)
{
string url = $"https://stream.companieshouse.gov.uk/companies?timepoint={startTimepoint}";
using (HttpResponseMessage response = await _httpClient.GetAsync(url, HttpCompletionOption.ResponseHeadersRead))
using (_streamToReadFrom = await response.Content.ReadAsStreamAsync())
{
string fileToWriteTo = Path.GetTempFileName();
using (Stream streamToWriteTo = System.IO.File.Open(fileToWriteTo, FileMode.Create))
{
await _streamToReadFrom.CopyToAsync(streamToWriteTo);
}
}
}
ThreadStart is a delegate representing parameterless method with void return value (so it it not async aware, i.e. the tread will not wait for task completion), while your method requires 2 parameters to be passed so from purely technical standpoint you can do something like new ThreadStart(() => GetCompaniesApi(1,2)), or just new Thread(() => GetCompaniesApi(1, 2)) (compiler will create the delegate for you). But you should not.
In modern .NET rarely there is need to create threads directly, just use Task.Run to schedule your async method on the thread pool (do not forget to provide the parameters):
await Task.Run(() => Test());
For async method - just invoke it:
var result = await GetCompaniesApi(someInt, someNullableLong);
If for some reason it is not actually async then better fix the method itself, but if needed you can wrap it into Task.Run too.
I think the question reveals a potential misunderstanding of multi threading and async.
The main purpose of async is to hide the latency of IO operations, like network or disk access. That helps reducing resource usage, and keeping the UI responsive.
Using background threads are mostly for hiding the latency of computations. For example if you are doing some slow image processing task. In that case you typically use Task.Run to execute a synchronous method, and await the result. Just make sure the method is thread safe.
While there are cases that mix these types of work and where you need to combine both methods, it is not that common. From your code I would guess your work is mostly IO related, so you should probably not use any backgrounds threads. Note that some libraries lie about being asynchronous, i.e. methods that return a task, but will still block. In that treating the method as a synchronous thread and use Task.Run to execute it might be warranted.
Also keep in mind that multi threaded programming is difficult. It introduces a great number of new types of faults, and most faults will not be caught by the compiler, and many are spurious and difficult to reproduce. So you really need to be aware of the dangers and know how to correctly use locks and other forms of synchronization.

Why is using Task.Run() still blocking my UI?

I have a synchronous method I am calling with Task.Run() and my UI is being blocked and unresponsive. The method loads information from a database via COM Interop and I don't have any control over that.
public List<EdmAddInInfo2> GetInstalledAddins()
{
IEdmAddInMgr7 addinMgr = m_vault as IEdmAddInMgr7;
Array installedAddins = Array.CreateInstance(typeof(EdmAddInInfo2), 0);
addinMgr.GetInstalledAddIns(out installedAddins);
if (installedAddins?.Length > 0)
return installedAddins.OfType<EdmAddInInfo2>().ToList();
return null;
}
I am calling the method this way when my form is shown;
private async void LicensesForm_Shown(object sender, EventArgs e)
{
var m_addins = await GetInstalledAddins().ConfigureAwait(false);
toolStripStatusLabel2.Text = $"Loaded {m_addins.Count} addins.";
}
private async Task<List<EdmAddInInfo2>> GetInstalledAddins()
{
AddinManager addinMgr = new AddinManager(Vault);
var addins = await Task.Run(() => addinMgr.GetInstalledAddins()).ConfigureAwait(false);
return addins;
}
Usually I would use a BCW and be on my way, but I figured I would give Tasks a shot. Any ideas?
After the discussion in comments, I'm thinking that there is something deeper going on involving COM. One thing to be aware of when involving COM is that it uses the Dispatcher for receiving events, which has bitten me before. If the issue is related to COM one would probably need more information about what is going on and that digging might not be worthwhile. I wish I could be more helpful, but I think I'll have to default to advising the easy way out. Launch a thread to call GetInstalledAddins, assign the result to a local variable, and notify the UI of completion through the Dispatcher.
Also, from my original answer before editing to add the above,
var m_addins = await GetInstalledAddins().ConfigureAwait(false);
should be:
var m_addins = await GetInstalledAddins().ConfigureAwait(true);
This is because on the next line you assign to a UI element's Text property. This assignment must be done from the UI thread, which is active when you call GetInstalledAddins(), but because you then call ConfigureAwait(false) the execution continues after await on any thread the async manager (I forget what it's called) chooses.
One of the strengths of using async/await in UI code is that execution can (in normal situations) be resumed on the same thread the await call was made on. This way you can continue accessing UI objects after the await. But your call to ConfigureAwait(false) instructs the async/await engine that you don't care which thread the execution is resumed on (but in this case you really should care that the execution is resumed on the same thread).
The method loads information from a database via COM Interop and I don't have any control over that.
Well then, depending on the implementation of that method, it may not be possible to unblock the UI thread.
However, you could try this: if that type is allocating COM objects in its constructor, they may be getting tied to the UI thread. I would try creating the instance on the background thread:
private Task<List<EdmAddInInfo2>> GetInstalledAddins()
{
return Task.Run(() => new AddinManager(Vault).GetInstalledAddins());
}
Usually I would use a BCW and be on my way
BackgroundWorker would have the exact same problem.

C# await tasks + infinite loop still freezing the UI

I am trying to get the proper 'structure' for monitoring the state of a game from external source(s) using (Tasks) async/await in order to run the tasks in an infinite loop, however the current way its written seems to just freeze up my UI.
What I have so far:
(in the "state machine" class)
// Start monitoring the game state for changes
public void Start()
{
tokenSource = new CancellationTokenSource();
CancellationToken token = tokenSource.Token;
IsRunning = true;
task = Task.Factory.StartNew(async () =>
{
while (true)
{
await Task.Run(()=>CheckForStateChange());
await Task.Delay(1000); // Pause 1 second before checking state again
}
}, token, TaskCreationOptions.LongRunning, TaskScheduler.FromCurrentSynchronizationContext());
}
Without the above "Task.Delay" line the UI completely freezes up. With the "Task.Delay" line it doesn't freeze up, but if I try to drag the window it skips back to where I began dragging it.
My assumption with the current code is that the 'await Task.Run()' executes and upon completion the 'await Task.Delay()' executes and then on completion returns to the beginning of the while(true) infinite loop. (ie. not running in parallel).
The CheckForStateChange() signature is as follows:
private void CheckForStateChange()
{
// ... A bunch of code to determine and update the current state value of the object
}
Nothing special there, simple non-async method. I have read through lots of examples / questions here on StackOverflow and I used to have CheckForStateChange as returning a Task (with awaitable actions inside the method) and many other iterations of code (with the same results).
Finally I call the Start() method from the main win32 form (button) as follows:
private void btnStartSW_Click(object sender, EventArgs e)
{
// Start the subscription of the event handler
if(!state.IsRunning)
{
state.StateChange += new SummonersWar.StateChangeHandler(OnGameStateChange);
state.Start();
}
}
I think the above code is the simplest form I have written the code structure in so far, but apparently its still not written 'properly'. Any help would be appreciated.
UPDATE:
The publisher side (state machine class):
// ------ Publisher of the event ---
public delegate void StateChangeHandler(string stateText);
public event StateChangeHandler StateChange;
protected void OnStateChange() // TODO pass text?
{
if (StateChange != null)
StateChange(StateText());
}
Where the StateText() method is just a temporary way of retrieving a 'text' representation of the current state (and is really a placeholder at this point until I organize it into a tidier struct)
IsRunning is purely a public bool.
And the handler in the UI thread:
private void OnGameStateChange(string stateText)
{
// Game State Changed (update the status bar)
labelGameState.Text = "State: " + stateText;
}
Why the UI freezes
In terms of the main question: you're already calling your CheckForStateChange via Task.Run, so there is no way that your CheckForStateChange will freeze the UI unless it includes calls which are marshalled back to the UI thread (i.e. Control.Invoke or SynchronizationContext.Post/Send used explicitly, or implicitly via a Task started on the UI TaskScheduler).
The best place to start looking is your StateChange handlers (i.e. StateChangeHandler). Also have a look at where the StateChange event is raised. You'll find thread marshalling code at one of these sites.
Other issues
You're passing the TaskScheduler pointing to the UI SynchronizationContext to the outer task. You're also passing in TaskCreationOptions.LongRunning. In simple terms you're telling the task factory to "start a task on a dedicated thread, and on the current thread". These two are mutually exclusive requirements and you can pretty safely drop them both.
If, as a result of the above, your outer task happens to execute on the UI thread, it won't really trip you up as the inner call is wrapped in Task.Run, but this probably isn't the behaviour you expect.
You are storing the result of Task.Factory.StartNew inside a task field or property. Note, however, that your Task.Factory.StartNew call returns a Task<Task>, so the saved Task instance will transition to completed state almost immediately unless you call Unwrap on it and get to the inner task. To avoid this entire mess, just use Task.Run to create the outer task (as it has Unwrap semantics built in). If you do that, you can ditch the inner Task.Run completely, like so:
public bool IsRunning
{
get
{
return task.Status == TaskStatus.Running;
}
}
public void Start()
{
tokenSource = new CancellationTokenSource();
CancellationToken token = tokenSource.Token;
task = Task.Run(async () =>
{
while (true)
{
CheckForStateChange(token);
token.ThrowIfCancellationRequested();
await Task.Delay(1000); // Pause 1 second before checking state again
}
}, token);
// Uncomment this and step through `CheckForStateChange`.
// When the execution hangs, you'll know what's causing the
// postbacks to the UI thread and *may* be able to take it out.
// task.Wait();
}
Since you have a CancellationToken you need to be passing it to CheckForStateChange, and checking it periodically - otherwise it only gets checked once, when the Task is started, and then never again.
Note that I have also provided a different IsRunning implementation. Volatile state is hard to get right. If the framework is giving it to you for free, you should use it.
Final word
Overall this entire solution feels like a bit of a crutch for something that should be done more reactively - but I can think of scenarios where this sort of design is valid. I'm just not convinced that yours is really one of them.
EDIT: how to find what's blocking the UI
I'll get downvoted to oblivion for this, but here goes:
The sure way to find what's causing postbacks to the UI thread is to deadlock with it. There's plenty of threads here on SO telling you how to avoid that, but in your case - we'll cause it on purpose and you'll know exactly what calls you need to avoid when you're polling for changes - although whether or not it will be possible to avoid these calls, remains to be seen.
I've put a task.Wait instruction at the end of my code snippet. Provided that you call Start on the UI thread, that should cause a deadlock with something inside your CheckForStateChange, and you will know what it is that you need to work around.

Why is this code running synchronously?

I am trying to understand concurrency by doing it in code. I have a code snippet which I thought was running asynchronously. But when I put the debug writeline statements in, I found that it is running synchronously. Can someone explain what I need to do differently to push ComputeBB() onto another thread using Task.Something?
Clarification I want this code to run ComputeBB in some other thread so that the main thread will keep on running without blocking.
Here is the code:
{
// part of the calling method
Debug.WriteLine("About to call ComputeBB");
returnDTM.myBoundingBox = await Task.Run(() => returnDTM.ComputeBB());
Debug.WriteLine("Just called await ComputBB.");
return returnDTM;
}
private ptsBoundingBox2d ComputeBB()
{
Debug.WriteLine("Starting ComputeBB.");
Stopwatch sw = new Stopwatch(); sw.Start();
var point1 = this.allPoints.FirstOrDefault().Value;
var returnBB = new ptsBoundingBox2d(
point1.x, point1.y, point1.z, point1.x, point1.y, point1.z);
Parallel.ForEach(this.allPoints,
p => returnBB.expandByPoint(p.Value.x, p.Value.y, p.Value.z)
);
sw.Stop();
Debug.WriteLine(String.Format("Compute BB took {0}", sw.Elapsed));
return returnBB;
}
Here is the output in the immediate window:
About to call ComputeBB
Starting ComputeBB.
Compute BB took 00:00:00.1790574
Just called await ComputBB.
Clarification If it were really running asynchronously it would be in this order:
About to call ComputeBB
Just called await ComputBB.
Starting ComputeBB.
Compute BB took 00:00:00.1790574
But it is not.
Elaboration
The calling code has signature like so: private static async Task loadAsBinaryAsync(string fileName) At the next level up, though, I attempt to stop using async. So here is the call stack from top to bottom:
static void Main(string[] args)
{
aTinFile = ptsDTM.CreateFromExistingFile("TestSave.ptsTin");
// more stuff
}
public static ptsDTM CreateFromExistingFile(string fileName)
{
ptsDTM returnTin = new ptsDTM();
Task<ptsDTM> tsk = Task.Run(() => loadAsBinaryAsync(fileName));
returnTin = tsk.Result; // I suspect the problem is here.
return retunTin;
}
private static async Task<ptsDTM> loadAsBinaryAsync(string fileName)
{
// do a lot of processing
Debug.WriteLine("About to call ComputeBB");
returnDTM.myBoundingBox = await Task.Run(() => returnDTM.ComputeBB());
Debug.WriteLine("Just called await ComputBB.");
return returnDTM;
}
I have a code snippet which I thought was running asynchronously. But when I put the debug writeline statements in, I found that it is running synchronously.
await is used to asynchronously wait an operations completion. While doing so, it yields control back to the calling method until it's completion.
what I need to do differently to push ComputeBB() onto another thread
It is already ran on a thread pool thread. If you don't want to asynchronously wait on it in a "fire and forget" fashion, don't await the expression. Note this will have an effect on exception handling. Any exception which occurs inside the provided delegate would be captured inside the given Task, if you don't await, there is a chance they will go about unhandled.
Edit:
Lets look at this piece of code:
public static ptsDTM CreateFromExistingFile(string fileName)
{
ptsDTM returnTin = new ptsDTM();
Task<ptsDTM> tsk = Task.Run(() => loadAsBinaryAsync(fileName));
returnTin = tsk.Result; // I suspect the problem is here.
return retunTin;
}
What you're currently doing is synchronously blocking when you use tsk.Result. Also, for some reason you're calling Task.Run twice, once in each method. That is unnecessary. If you want to return your ptsDTM instance from CreateFromExistingFile, you will have to await it, there is no getting around that. "Fire and Forget" execution doesn't care about the result, at all. It simply wants to start whichever operation it needs, if it fails or succeeds is usually a non-concern. That is clearly not the case here.
You'll need to do something like this:
private PtsDtm LoadAsBinary(string fileName)
{
Debug.WriteLine("About to call ComputeBB");
returnDTM.myBoundingBox = returnDTM.ComputeBB();
Debug.WriteLine("Just called ComputeBB.");
return returnDTM;
}
And then somewhere up higher up the call stack, you don't actually need CreateFromExistingFiles, simply call:
Task.Run(() => LoadAsBinary(fileName));
When needed.
Also, please, read the C# naming conventions, which you're currently not following.
await's whole purpose is in adding the synchronicity back in asynchronous code. This allows you to easily partition the parts that are happenning synchronously and asynchronously. Your example is absurd in that it never takes any advantage whatsoever of this - if you just called the method directly instead of wrapping it in Task.Run and awaiting that, you would have had the exact same result (with less overhead).
Consider this, though:
await
Task.WhenAll
(
loadAsBinaryAsync(fileName1),
loadAsBinaryAsync(fileName2),
loadAsBinaryAsync(fileName3)
);
Again, you have the synchronicity back (await functions as the synchronization barrier), but you've actually performed three independent operations asynchronously with respect to each other.
Now, there's no reason to do something like this in your code, since you're using Parallel.ForEach at the bottom level - you're already using the CPU to the max (with unnecessary overhead, but let's ignore that for now).
So the basic usage of await is actually to handle asynchronous I/O rather than CPU work - apart from simplifying code that relies on some parts of CPU work being synchronised and some not (e.g. you have four threads of execution that simultaneously process different parts of the problem, but at some point have to be reunited to make sense of the individual parts - look at the Barrier class, for example). This includes stuff like "making sure the UI doesn't block while some CPU intensive operation happens in the background" - this makes the CPU work asynchronous with respect to the UI. But at some point, you still want to reintroduce the synchronicity, to make sure you can display the results of the work on the UI.
Consider this winforms code snippet:
async void btnDoStuff_Click(object sender, EventArgs e)
{
lblProgress.Text = "Calculating...";
var result = await DoTheUltraHardStuff();
lblProgress.Text = "Done! The result is " + result;
}
(note that the method is async void, not async Task nor async Task<T>)
What happens is that (on the GUI thread) the label is first assigned the text Calculating..., then the asynchronous DoTheUltraHardStuff method is scheduled, and then, the method returns. Immediately. This allows the GUI thread to do whatever it needs to do. However - as soon as the asynchronous task is complete and the GUI is free to handle the callback, the execution of btnDoStuff_Click will continue with the result already given (or an exception thrown, of course), back on the GUI thread, allowing you to set the label to the new text including the result of the asynchronous operation.
Asynchronicity is not an absolute property - stuff is asynchronous to some other stuff, and synchronous to some other stuff. It only makes sense with respect to some other stuff.
Hopefully, now you can go back to your original code and understand the part you've misunderstood before. The solutions are multiple, of course, but they depend a lot on how and why you're trying to do what you're trying to do. I suspect you don't actually need to use Task.Run or await at all - the Parallel.ForEach already tries to distribute the CPU work over multiple CPU cores, and the only thing you could do is to make sure other code doesn't have to wait for that work to finish - which would make a lot of sense in a GUI application, but I don't see how it would be useful in a console application with the singular purpose of calculating that single thing.
So yes, you can actually use await for fire-and-forget code - but only as part of code that doesn't prevent the code you want to continue from executing. For example, you could have code like this:
Task<string> result = SomeHardWorkAsync();
Debug.WriteLine("After calling SomeHardWorkAsync");
DoSomeOtherWorkInTheMeantime();
Debug.WriteLine("Done other work.");
Debug.WriteLine("Got result: " + (await result));
This allows SomeHardWorkAsync to execute asynchronously with respect to DoSomeOtherWorkInTheMeantime but not with respect to await result. And of course, you can use awaits in SomeHardWorkAsync without trashing the asynchronicity between SomeHardWorkAsync and DoSomeOtherWorkInTheMeantime.
The GUI example I've shown way above just takes advantage of handling the continuation as something that happens after the task completes, while ignoring the Task created in the async method (there really isn't much of a difference between using async void and async Task when you ignore the result). So for example, to fire-and-forget your method, you could use code like this:
async void Fire(string filename)
{
var result = await ProcessFileAsync(filename);
DoStuffWithResult(result);
}
Fire("MyFile");
This will cause DoStuffWithResult to execute as soon as result is ready, while the method Fire itself will return immediately after executing ProcessFileAsync (up to the first await or any explicit return someTask).
This pattern is usually frowned upon - there really isn't any reason to return void out of an async method (apart from event handlers); you could just as easily return Task (or even Task<T> depending on the scenario), and let the caller decide whether he wants his code to execute synchronously in respect to yours or not.
Again,
async Task FireAsync(string filename)
{
var result = await ProcessFileAsync(filename);
DoStuffWithResult(result);
}
Fire("MyFile");
does the same thing as using async void, except that the caller can decide what to do with the asynchronous task. Perhaps he wants to launch two of those in parallel and continue after all are done? He can just await Task.WhenAll(Fire("1"), Fire("2")). Or he just wants that stuff to happen completely asynchronously with respect to his code, so he'll just call Fire("1") and ignore the resulting Task (of course, ideally, you at the very least want to handle possible exceptions).

How to create a DispatcherObject asynchronously with async/await?

Clearly, there is something I am not understandig with async/await.
What is wrong with the following code? It creates the FDecoder object in an async task. But after that, whenever I try to access the FDecoder field I get an InvalidOperation exception stating that the object is owned by another thread. I thought that's the cool thing about await, that i get the results back into the calling thread...?
//could take very long for image from web
private Task<GifBitmapDecoder> OpenFileTask(string filename, bool forceReload = false)
{
return Task.Run(() =>
{
return new GifBitmapDecoder(new Uri(filename, UriKind.RelativeOrAbsolute), forceReload ? BitmapCreateOptions.IgnoreImageCache : BitmapCreateOptions.None, BitmapCacheOption.Default);
});
}
GifBitmapDecoder FDecoder;
public async void OpenFileAsync(string filename, bool forceReload = false)
{
FDecoder = await OpenFileTask(filename, forceReload);
OpenCompleted(); // do stuff with FDecoder field, throws invalid thread exception
}
EDIT:
Ok, what i found out is that the actual GifBitmapDecoder object the Task creates is a DispatcherObject which has thread affinity. This is the main problem... It appears that the only way is to get all needed data out of the Dispatcher object in the async task and pass back a normal object without thread affinity. But if anyone knows a better method, please tell me.
You always end up back in the same context, but not all contexts are tied to a single thread. Notably, the Thread Pool context treats all thread pool threads as being equal.
But I don't think that that's the specific issue here - you're using Task.Run() which is meant to run code in the thread pool. So even if your await switches everything back into the UI context, it doesn't matter because you run some of the code, explicitly, in the thread pool.
This is an interesting problem, because (as you rightly point out) GifBitmapDecoder inherits from DispatcherObject. This means it has an implementation which does not allow just any thread to invoke its operations.
To work with any DispatcherObject you should make calls through its Dispatcher property. The returned Dispatcher object lets you schedule delegates against the real object in a way that's compatible with its internal threading model via InvokeAsync:
var decoder = new GifBitmapDecoder(...);
var operation = decoder.Dispatcher.InvokeAsync(() => { }); // Do things here!
This pattern, rather than returning a TPL Task returns a DispatcherOperation (presumably because it pre-dates TPL). This very task-like object lets you examine the state of the operation and get any results. It's also awaitable, meaning you can use it with await just like a TPL Task:
await decoder.Dispatcher.InvokeAsync(() => { });
In your specific problem, you should use this pattern in your OpenCompleted() method. You will probably want to make it OnCompletedAsync() and return a Task to enable you to capture the UI Synchronization Context for your continuations and let the TPL handle marshalling calls back from the Dispatcher to the UI thread.
public async void OpenFileAsync(string filename, bool forceReload = false)
{
FDecoder = await OpenFileTask(filename, forceReload);
await OpenCompletedAsync();
}
Task.Run() schedules to the threadpool, so your GifBitmapDecoder is being created on a different thread.
OpenFileTask is returning a task<GifBitMapDecoder>. You will likely need
Task <GifBitMapDecoder> t = OpenFileTask();
Fdecoder = t.result; //Returns the GifBitMapDecoder object.
Don't know much about the async stuff though, but probably is the same as you have it.
Source:C#5.0 in a nutshell.

Categories