I want to implement a file copying process asynchronously in C# using async/await/Task<T>. The process should be non-blocking for the UI, so will be run in a separate Task. There are situations though, when the process needs to be paused and synchronous action must be performed.
For instance, it may happen, that a file, which is being copied, already exists in the target location. In this case, a modal dialog should appear, asking user what to do. The dialog should be modal in respect to the window, which shows progress of copying, and since showing it is a UI operation, it should be shown from the main thread. Moreover, if multiple copying tasks encounter the same situation, they should be handled one-by-one, not all at the same time.
What is the proper way of "synchronously pausing" a running task?
My only idea looks like following:
Task keeps its whole state in a class instance
When such situation happens, it terminates, returning a class containing:
Information on what happened
State instance
This finishes the await call and caller can synchronously react to whatever happened in the Task.
When user decides to continue process (e.g. replace the target file or rename copied one), the Task is re-executed with the state returned from the previous execution along with user's decision.
The pseudocode for this solution would look like following:
public async Task ExecuteProcess()
{
UserDecision lastDecision = null;
var state = new();
bool finished = false;
while (!finished)
{
var result = await DoExecuteProcess(state, lastDecision);
if (result is UserChoiceResult)
{
lastDecision = AskUser();
}
else if (result is FinishedResult)
{
finished = true;
}
else
throw new InvalidOperationException("Unsupported async task result");
}
}
I'm wondering though, if there is a better solution for this problem, as this one seems a little bit sketchy for me (returning a "partially-done-requiring-more-information" result from async task).
Related
I'm having trouble understanding what runs on the main thread during an async await operation and would be grateful for some answers.
Let's say I have a button that is supposed to log the user in.
it is supposed to block all other user input while the login process transpires, show a progress view and then when the result comes in display it
and here is the method that performs the log in
button_clicked(object sender, EventArgs e) {
do_login(); //I do not await the result
do_some_other_stuff(); //this doesn't actually exist I just put it here to ask my questions
}
async Task do_login() {
string user_name = txtUser.Text;
string password = txtPassword.Text;
show_progress(true); //this is not an async method;
string error = await _login.do_login(user_name, password);//this is an async method that can take up to 20 seconds to complete;
show_progress(false);
if (error != null) {
show_error(error);
} else {
show_next_screen();
}
}
I have two questions on the above example
a) What will be run on the main thread?
If I understand it correctly only _login.do_login will be run on a seperate thread, all others will be on the main thread, is this correct?
b) In what order will the methods be executed?
Again if I understand it correctly, it will be :
do_login()
show_progress(true);
_login.do_login starts;
do_some_other_stuff();
_login.do_login finishes;
show_progress(false);
and it will continue from there
is this correct? if not, how can I achieve such a behaviour?
c) If my code above is correct then why do I keep receiving a warning that do_login() is not awaited? I do not wish to await it I just want it to run what it can and return when it wants, should I ignore that warning?
Technically, depending on the implementation of do_login, everything could run in the main thread. In this case I assume you're contacting a web server, so that part won't, but this is not always true. And asynchronous operation does not necessarily executes in another thread. One operation is asynchronous when:
It doesn't block the calling thread.
Usually, UI threads run an 'event loop'. So an asynchronous task could simply put a new piece of work into the event queue to be executed whenever the scheduler determines, but in the same thread. In this case you don't use two threads, but still, you don't have to wait for the task to complete and you don't know when it'll finish.
To be precise, all the code in your post will run in the main thread. Only the part in do_login that manages the connection with the server, waiting and retrieving data will execute asynchronously.
You're mostly right about the sequence, with a few adjustments:
do_login() (until the await)
login._do_login() starts executing
do_some_other_stuff()
...
login.do_login finishes
show_progress()
The answer to your main question is: it depends. The _login.do_login method will likely be put onto its own thread, but it actually depends on the .NET task scheduler. In WPF and ASP.NET it will be scheduled onto the thread pool if it doesn't immediately return a completed task.
The important part is that you know it will not block execution of the calling (in your case, the main) thread. Your understanding of the method flow is correct since you don't await do_login.
As far as the warning goes; you can mark do_login as async void to avoid it, though generally you only do that for event handlers which can then await a Task returning method. If you do go the async void route; make sure to put a try/catch in as such methods will throw all the way up to the root handler and can cause your app to crash.
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.
First of all, let me describe the flow of the program I am writing.
There is a list of objects in the GUI and when the user clicks on one, the object data is read from the disk and is loaded. This could take around 3-4 seconds.
Let's say the user is impatient and clicks on another object while the first one is still loading. The program will load the second object and at the same time, will cancel the loading for the first object.
As the user could spam load operations before any of them could complete successfully, I have implemented an action block to queue up all the load operations.
So I have an action block like this:
this.loadObjectActionBlock = new ActionBlock<CaseObject>(
c => this.LoadCaseData(CaseObject),
new ExecutionDataflowBlockOptions()
{
MaxDegreeOfParallelism = 10,
});
Every time the user has clicked on a Case Object, I will call:
this.loadObjectActionBlock.Post(CaseObject);
And the CaseObject will be processed by a function I have defined like this:
private void LoadCaseData(CaseObject caseObject)
{
caseObject.LoadCaseData();
}
What I need to do right now is, I need to wait until ALL CaseObjects are loaded so that I could continue my code after that.
I have tried detecting when all cases are processed by calling
if (this.loadObjectActionBlock.InputCount == 0)
{
this.loadObjectActionBlock.Complete();
}
after caseObject.LoadCaseData(); but this leads to strange results when load actions happen way too fast and the action block is told not to accept any more inputs. If I understand correctly, the InputCount property only looks at the number of jobs left in the queue.
So what I'd like to do is I'd like to await the ActionBlock like:
await this.loadObjectActionBlock.Completion;
when everything in the queue has been fully processed.
I may not be using the ActionBlock as it is intended to, so if there are any alternatives on this, please kindly suggest anything that I could read up on.
TLDR: I'd like to process multiple tasks (started by user) concurrently and wait for all to be complete then followed by a single task.
Thanks in advance :)
The program will load the second object and at the same time, will cancel the loading for the first object.
A queue is not the appropriate solution for this behavior, especially since a TPL block can only be completed once.
If you want to implement this behavior, simply ensure the cancellation token is observed before continuing with the next operation:
private static void ProcessCase(CaseObject caseObject, CancellationToken token)
{
caseObject.LoadCaseData();
token.ThrowIfCancellationRequested();
... // Further processing goes here
}
Called from the UI thread as:
static CancellationTokenSource _cts;
private static async Task ProcessCaseAsync(CaseObject caseObject)
{
if (_cts != null)
_cts.Cancel();
_cts = new CancellationTokenSource();
await Task.Run(() => ProcessCase(caseObject, _cts.Token));
}
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).
I am trying to implement a method called ReadAllLinesAsync using the async feature. I have produced the following code:
private static async Task<IEnumerable<string>> FileReadAllLinesAsync(string path)
{
using (var reader = new StreamReader(path))
{
while ((await reader.ReadLineAsync()) != null)
{
}
}
return null;
}
private static void Main()
{
Button buttonLoad = new Button { Text = "Load File" };
buttonLoad.Click += async delegate
{
await FileReadAllLinesAsync("test.txt"); //100mb file!
MessageBox.Show("Complete!");
};
Form mainForm = new Form();
mainForm.Controls.Add(buttonLoad);
Application.Run(mainForm);
}
I expect the listed code to run asynchronously and as a matter of fact, it does! But only when I run the code without the Visual Studio Debugger.
When I run the code with the Visual Studio Debugger attached, the code runs synchronously, blocking the main thread causing the UI to hang.
I have attempted and succeeded to reproduce the problem on three machines. Each test was conducted on a 64bit machine (either Windows 8 or Windows 7) using Visual Studio 2012.
I would like to know why this problem is occuring and how to solve it (as running without the debugger will likely hinder development).
The problem is that you are calling await reader.ReadLineAsync() in a tight loop that does nothing - except return execution to the UI thread after each await before starting all over again. Your UI thread is free to process windows events ONLY while ReadLineAsync() tries to read a line.
To fix this, you can change the call to await reader.ReadLineAsync().ConfigureAwait(false).
await waits for the completion of an asynchronous call and returns execution to the Syncrhonization context that called await in the first place. In a desktop application, this is the UI thread. This is a good thing because it allows you to update the UI directly but can cause blocking if you process the results of the asynchronous call right after the await.
You can change this behavior by specifying ConfigureAwait(false) in which case execution continues in a different thread, not the original Synchronization context.
Your original code would block even if it wasn't just a tight loop, as any code in the loop that processed the data would still execute in the UI thread. To process the data asynchronously without adding ConfigureAwait, you should process the data in a taks created using eg. Task.Factory.StartNew and await that task.
The following code will not block because processing is done in a different thread, allowing the UI thread to process events:
while ((line= await reader.ReadLineAsync()) != null)
{
await Task.Factory.StartNew(ln =>
{
var lower = (ln as string).ToLowerInvariant();
Console.WriteLine(lower);
},line);
}
I'm seeing the same problem as you to an extent - but only to an extent. For me, the UI is very jerky in the debugger, and occasionally jerky not in the debugger. (My file consists of lots of lines of 10 characters, by the way - the shape of the data will change behaviour here.) Often in the debugger it's good to start with, then bad for a long time, then it sometimes recovers.
I suspect the problem may simply be that your disk is too fast and your lines are too short. I know that sounds crazy, so let me explain...
When you use an await expression, that will only go through the "attach a continuation" path if it needs to. If the results are present already, the code just extracts the value and continues in the same thread.
That means, if ReadLineAsync always returns a task which is completed by the time it returns, you'll effectively see synchronous behaviour. It's entirely possible that ReadLineAsync looks at what data it's already got buffered, and tries to synchronously find a line within it to start with. The operating system may well then read more data from the disk so that it's ready for your application to use... which means that the UI thread never gets a chance to pump its normal messages, so the UI freezes.
I had expected that running the same code over a network would "fix" the problem, but it didn't seem to. (It changes exactly how the jerkiness is shown, mind you.) However, using:
await Task.Delay(1);
Does unfreeze the UI. (Task.Yield doesn't though, which again confuses me a lot. I suspect that may be a matter of prioritization between the continuation and other UI events.)
Now as for why you're only seeing this in the debugger - that still confuses me. Perhaps it's something to do with how interrupts are processed in the debugger, changing the timing subtly.
These are only guesses, but they're at least somewhat educated ones.
EDIT: Okay, I've worked out a way to indicate that it's at least partly to do with that. Change your method like this:
private static async Task<IEnumerable<string>>
FileReadAllLinesAsync(string path, Label label)
{
int completeCount = 0;
int incompleteCount = 0;
using (var reader = new StreamReader(path))
{
while (true)
{
var task = reader.ReadLineAsync();
if (task.IsCompleted)
{
completeCount++;
}
else
{
incompleteCount++;
}
if (await task == null)
{
break;
}
label.Text = string.Format("{0} / {1}",
completeCount,
incompleteCount);
}
}
return null;
}
... and create and add a suitable label to the UI. On my machine, both in debug and non-debug, I see far more "complete" hits than "incomplete" - oddly enough, the ratio of complete to incomplete is 84:1 consistently, both under the debugger and not. So it's only after reading about one in 85 lines that the UI can get a chance to update. You should try the same on your machine.
As another test, I added a counter incrementing in the label.Paint event - in the debugger it only executed 1/10th as many times as not in the debugger, for the same number of lines.
Visual Studio isn't actually executing the asynchronous callback synchronously. However, your code is structured in such a manner that it is "flooding" the UI thread with messages that you may not need to execute on a UI thread. Specifically, when FileReadAllLinesAsync resumes execution in the body of the while loop, it does so on the SynchronizationContext that was captured on the await line in the same method. What this means is for every line in your file, a message is posted back to the UI thread to execute 1 copy of the body of that while loop.
You can resolve this issue by using ConfigureAwait(false) carefully.
In FileReadAllLinesAsync, the body of the while loop is not sensitive to which thread it runs on, so you can use the following instead:
while ((await reader.ReadLineAsync().ConfigureAwait(false)) != null)
In Main, suppose you do want the MessageBox.Show line to execute on the UI thread (perhaps you also have a buttonLoad.Enabled = true statement there). You can (and will!) still get this behavior without any changes to Main, since you did not use ConfigureAwait(false) there.
I suspect the delays you observe in the debugger are due to .NET's slower performance in managed/unmanaged code while a debugger is attached, so dispatching each of those millions of messages to the UI thread is up to 100x slower when you have the debugger attached. Rather than try to speed up that dispatching by disabling features, I suspect item #1 above will resolve the bulk of your problems immediately.
From Task-based Asynchronous Pattern in Microsoft Download Center :
For performance reasons, if a task has already completed by the time
the task is awaited, control will not be yielded, and the function
will instead continue executing.
And
In some cases, the amount of work required to complete the operation
is less than the amount of work it would take to launch the operation
asynchronously (e.g. reading from a stream where the read can be
satisfied by data already buffered in memory). In such cases, the
operation may complete synchronously, returning a Task that has
already been completed.
So my last answer was incorrect (short-timing asynchronous operation is synchronous for performance reasons).