Baffled by Async, Await and Task - need guidance - c#

I'm afraid the more I read, the more baffled I get. I think I need a real world example. I'm stuck in some code in a MVVM WPF app. How do I get NetworkPlaces running asyncronously so it doesn't block the UI thread? I've tried loads of different combinations of async, task and await.
A lot of the examples seem to use 'end' methods that have asynchronous methods within it such as .ReadToEndAsync or .GetStringAsync. One advisory (and UI freeze) I kept getting was about NetworkPlaces not having an Async method.
Here's my illustration code at the moment. I'd be grateful if someone could show mw some working code:
public RelayCommand ScanForDevicesCMD { get; private set; }
public MainVM()
{
ScanForDevicesCMD = new RelayCommand(ScanForDevices, CanScanForDevices);
}
public async void ScanForDevices(object obj)
{
Debug.WriteLine("SCAN STARTED");
Task task = NetworkPlaces();
await task;
Debug.WriteLine("SCAN STOPPED");
}
public Task NetworkPlaces()
{
ShellNetworkComputers shellNetworkComputers = new ShellNetworkComputers();
foreach (string snc in shellNetworkComputers)
{
Debug.WriteLine(snc);
}
return Task.CompletedTask;
}

You can run synchronous code as a Task.
Change your method to this:
public void NetworkPlaces()
{
ShellNetworkComputers shellNetworkComputers = new ShellNetworkComputers();
foreach (string snc in shellNetworkComputers)
{
Debug.WriteLine(snc);
}
}
And call it like this:
await Task.Run(() => {
NetworkPlaces();
});
Update: Make sure that NetworkPlaces() doesn't do any UI work (e.g. user interaction) itself.

Related

Do Multi delegate Async Actions Only work with Begin Invoke

I'm trying to understand async actions and I'm a bit confused.
Actions are just glorified Delegates. Given the Actions
Action act = null;
act += () => { Console.WriteLine("Sync"); };
act += async () => { await File.AppendAllLinesAsync("C:/Test.Txt",
new[] { "Async File Operation" });
};
How can we invoke this async seeing as one of the delegates is async and the other is not. I've seen some extension methods in other SO answers simplified for the example would look like so:
public static void InvokeAsync(this Action action, AsyncCallback ar, object userObject = null)
{
var listeners = action.GetInvocationList();
foreach (var t in listeners)
{
var handler = (Action)t;
handler.BeginInvoke(ar, userObject);
}
}
I'm concerned if this even works because it looks like it invokes your callback for each listener which doesn't make sense.
I've only been using async with the more friendly version async/await so I do not understand this syntax as much. (I'm assuming the callback would be everything after the await and the userObject is equivalent to the dreadful SyncronizationContext that causes deadlocks if when calling sync without ConfigureAwait(false), but that is just a guess)
This is syntax inconvenient so I would perfer to use async await syntax, since async/await is called using duck-typing. I've read a blog about using async with delegates which for the example
public static class DelegateExtensions
{
public static TaskAwaiter GetAwaiter(this Action action)
{
Task task = new Task(action);
task.Start();
return task.GetAwaiter();
}
}
This too concerns me for a few reason, this looks much like an anti pattern.
Isn't this just creating a task which will run my action synchronous on a seperate thread? I also don't see this run through the invocation list.
Are either of these methods proper for invoking run delegates asynchronously?
Is there a way I can invoke an async delegate with the await syntax while still fully leveraging async?
What is the proper way to invoke async delegates with multiple functions in the invocation list?
I think Eric Lippert's comment have clarified the situation more than I could ever.
Overall, if you need to act on the return type of a method, you shouldn't use multicast delegates. If you still have to, at least use a Func<Task> signature, then you can iterate on each individual delegate using GetInvocationList, as explained here.
But would it be really impossible to work your way out of a multicast delegate with async void method?
It turns out that you can be notified of beginning and end of async void methods by using a custom synchronization context and overriding the OperationStarted and OperationCompleted methods. We can also override the Post method to set the synchronization context of child operations, to capture subsequent async void calls.
Piecing it together, you could come with something like:
class Program
{
static async Task Main(string[] args)
{
Action act = null;
act += () => { Console.WriteLine("Sync"); };
act += async () =>
{
Callback();
await Task.Delay(1000);
Console.WriteLine("Async");
};
await AwaitAction(act);
Console.WriteLine("Done");
Console.ReadLine();
}
static async void Callback()
{
await Task.Delay(2000);
Console.WriteLine("Async2");
}
static Task AwaitAction(Action action)
{
var delegates = action.GetInvocationList();
var oldSynchronizationContext = SynchronizationContext.Current;
var asyncVoidSynchronizationContext = new AsyncVoidSynchronizationContext();
try
{
SynchronizationContext.SetSynchronizationContext(asyncVoidSynchronizationContext);
var tasks = new Task[delegates.Length];
for (int i = 0; i < delegates.Length; i++)
{
((Action)delegates[i]).Invoke();
tasks[i] = asyncVoidSynchronizationContext.GetTaskForLastOperation();
}
return Task.WhenAll(tasks);
}
finally
{
SynchronizationContext.SetSynchronizationContext(oldSynchronizationContext);
}
}
}
public class AsyncVoidSynchronizationContext : SynchronizationContext
{
private TaskCompletionSource<object> _tcs;
private Task _latestTask;
private int _operationCount;
public Task GetTaskForLastOperation()
{
if (_latestTask != null)
{
var task = _latestTask;
_latestTask = null;
return task;
}
return Task.CompletedTask;
}
public override void Post(SendOrPostCallback d, object state)
{
Task.Run(() =>
{
SynchronizationContext.SetSynchronizationContext(this);
d(state);
});
}
public override void OperationStarted()
{
if (Interlocked.Increment(ref _operationCount) == 1)
{
// First operation
_tcs = new TaskCompletionSource<object>();
_latestTask = _tcs.Task;
}
base.OperationStarted();
}
public override void OperationCompleted()
{
if (Interlocked.Decrement(ref _operationCount) == 0)
{
// Last operation
_tcs.TrySetResult(null);
}
base.OperationCompleted();
}
}
The output would be:
Sync
Async
Async2
Done
Of course, this code is provided just for recreational purpose. There's plenty of limitations, such as the fact the fact that it wouldn't work as-is if you're already using a synchronization context (such as the WPF one). I'm also certain that it has a few subtle bugs and concurrency issues here and there.

Multiple infinte loops using async and await

I am trying to generate different objects and insert each object into it's respective List using await and async using the code below:
static List<ClassA> classAList = new List<ClassA>();
static List<ClassB> classBList = new List<ClassB>();
public async void GenerateUsers()
{
await GenerateClassA();
await GenerateClassB();
}
private static Task GenerateClassA()
{
while(true)
{
Thread.Sleep(3000);
classAList .Add(new ClassA());
Console.WriteLine(classAList.Count);
}
}
private static Task GenerateClassB()
{
while (true)
{
Thread.Sleep(6000);
classBList .Add(new ClassB());
Console.WriteLine(classBList.Count)
}
}
When I call GenerateUsers like
UserContainer uc = new UserContainer(); //Class GenerateUsers is defined
uc.GenerateUsers();
Console.WriteLine("Generating.....");
The program does not exit GenerateClassA and Generating..... is not printed to the screen.
How can I generate different objects in an infinite loop using await and async for each infinite loop.
You are using await GenerateClassA();, but GenerateClassA doesn't do anything continuation related - it simply run to completion promising to return a Task. Except: it can't run to completion, because while(true). So yes, this won't work.
Something doesn't become async just beacause you add async. That enables genuinely async operations to be coordinated: nothing more, nothing less.
The methods you are calling are not async, and so they execute synchronously. Indeed, only the first method, GenerateClassA(), is ever even called. That method never returns, so you never get as far as calling the second. Probably you wanted something more like this:
public void GenerateUsers()
{
GenerateClassA();
GenerateClassB();
}
private static async void GenerateClassA()
{
while(true)
{
await Task.Delay(3000);
classAList.Add(new ClassA());
Console.WriteLine(classAList.Count);
}
}
private static async void GenerateClassB()
{
while (true)
{
await Task.Delay(6000);
classBList.Add(new ClassB());
Console.WriteLine(classBList.Count)
}
}
Note that the void return types prevent you from observing the tasks, such as exceptions that might occur. It's not advised. But it's consistent with the code you originally posted.

Waiting for Async calls to finish

I'm working with the Philips Hue, and I need to get some information from the hue bridge before I can populate my application. The requests are made via HTTP/JSON. I have no issue when I run all my code async, but when I try to break out my code so that I have a separate method to update the UI upon loading I'm getting a System.NullReferenceException on myLights. I'm assuming that's because my startUpProcedure() isn't finished yet, hence myLights has not been set. I can't seem to figure out how to wait for the startUpProcedure() to finish before I run setupUI().
To further back that up, if I just run startUpProcedure() without setupUI() I get no issues. And then if I run setupUI() from say a button click it runs just fine.
Clearly I'm missing something here. I just can't seem to find the answer.
So to succinctly put the question: How do I wait for these Async calls to finish so that I can use variables that are depended on their return values?
public sealed partial class MainPage : Page
{
public string appKey;
public string myIP;
public IEnumerable<Light> myLights;
public ILocalHueClient myClient;
public MainPage()
{
this.InitializeComponent();
startUpPocedure();
setupUI();
}
public async Task startUpPocedure()
{
await startUp();
await getLights();
}
public async Task startUp()
{
if (await findBridgeIP())
{
Debug.WriteLine("Bridge Found...");
//Do Actions
}
else
{
//Error!!
Debug.WriteLine("No hue found");
}
Windows.Storage.ApplicationDataContainer localSettings = Windows.Storage.ApplicationData.Current.LocalSettings;
Windows.Storage.StorageFolder localFolder = Windows.Storage.ApplicationData.Current.LocalFolder;
try {
appKey = localSettings.Values["appKey"].ToString();
Debug.WriteLine("appKey loaded: " + appKey);
//Load up
myClient = new LocalHueClient(myIP);
myClient.Initialize(appKey);
}
catch {
Debug.WriteLine("Need to register app");
}
}
async Task getLights()
{
myLights = await myClient.GetLightsAsync();
Debug.WriteLine("Light Count " + myLights.Count());
IEnumerable<string> myLightNames;
List<string> myTempList = new List<string>();
foreach (Light l in myLights)
{
myTempList.Add(l.Name);
Debug.WriteLine(l.Name);
}
myLightNames = myTempList;
comboBox_LightSelect.ItemsSource = myLightNames;
}
private void setupUI()
{
//Populate the Combo Box
IEnumerable<string> myLightNames;
List<string> myTempList = new List<string>();
foreach (Light l in myLights)
{
myTempList.Add(l.Name);
Debug.WriteLine(l.Name);
}
myLightNames = myTempList;
comboBox_LightSelect.ItemsSource = myLightNames;
}
Your startUpPocedure method in MainPage constructor returns Task almost immediately, and because you're not awaiting it, code execution goes to the next line right after that and setupUI gets called. So when your code starts to enumerate myLights collection, it's still null because startUpPocedure and therefore getLights are still running.
Problem is, you can't await asynchronous methods in a constructor, so in your case solution will be to move both startUpPocedure and setupUI to single async method, await them inside this method and call it from constructor similar to this:
public MainPage()
{
this.InitializeComponent();
Startup();
}
private async void Startup()
{
await startUpPocedure();
setupUI();
}
You should leave only InitializeComponent in the constructor, and move all other logic to Loaded event handler,
https://msdn.microsoft.com/en-us/library/windows/apps/windows.ui.xaml.frameworkelement.loaded
Then you can mark that handler as async, and use await in it to await on async methods.

Async methods and progress indicator

I have a silverlight application which is making multiple async calls:
The problem I am facing is to how to determine if all the async calls are finished so that I can stop displaying the progress indicator. In the example below, progress indicator is stopped as soon as the first async method returns.
Any tips on how to resolve this ?
Constructor()
{
startprogressindicator();
callasync1(finished1);
callasync2(finished2);
//.... and so on
}
public void finished1()
{
stopprogressindicator();
}
public void finished2()
{
stopprogressindicator();
}
You need to asynchronously wait for both methods to finish, currently you call stopprogressindicator as soon as any of the method completes.
Refactor your code to return Task from callasync1 and callasync2 Then you can do
var task1 = callasync1();
var task2 = callasync2();
Task.Factory.ContinueWhenAll(new []{task1, task2}, (antecedents) => stopprogressindicator());
I do like the idea of using Task API, but in this case you may simply use a counter:
int _asyncCalls = 0;
Constructor()
{
startprogressindicator();
Interlocked.Increment(ref _asyncCalls);
try
{
// better yet, do Interlocked.Increment(ref _asyncCalls) inside
// each callasyncN
Interlocked.Increment(ref _asyncCalls);
callasync1(finished1);
Interlocked.Increment(ref _asyncCalls);
callasync2(finished2);
//.... and so on
}
finally
{
checkStopProgreessIndicator();
}
}
public checkStopProgreessIndicator()
{
if (Interlocked.Decrement(ref _asyncCalls) == 0)
stopprogressindicator();
}
public void finished1()
{
checkStopProgreessIndicator()
}
public void finished2()
{
checkStopProgreessIndicator()
}

await not blocking until Task finishes

I am trying to block RequestHandler.ParseAll() with await ConsumerTask;, but when i set a breakpoint there, i ALWAYS get the "Done..." output first... and then Parse2() fails with a NullReferenceException. (thats my guess: "the GC starts cleaning up because _handler got out of scope")
Anyway, I can't figure out why that happens.
class MainClass
{
public async void DoWork()
{
RequestHandler _handler = new RequestHandler();
string[] mUrls;
/* fill mUrls here with values */
await Task.Run(() => _handler.ParseSpecific(mUrls));
Console.WriteLine("Done...");
}
}
static class Parser
{
public static async Task<IEnumerable<string>> QueryWebPage(string url) { /*Query the url*/ }
public static async Task Parse1(Query query)
{
Parallel.ForEach(/*Process data here*/);
}
public static async Task Parse2(Query query)
{
foreach(string line in query.WebPage)
/* Here i get a NullReference exception because query.WebPage == null */
}
}
sealed class RequestHandler
{
private BlockingCollection<Query> Queue;
private Task ConsumerTask = Task.Run(() => /* call consume() for each elem in the queue*/);
private async void Consume(Query obj)
{
await (obj.BoolField ? Parser.Parse1(obj) : Parser.Parse2(obj));
}
public async void ParseSpecific(string[] urls)
{
foreach(string v in urls)
Queue.Add(new Query(await QueryWebPage(v), BoolField: false));
Queue.CompleteAdding();
await ConsumerTask;
await ParseAll(true);
}
private async Task ParseAll(bool onlySome)
{
ReInit();
Parallel.ForEach(mCollection, v => Queue.Add(new Query(url, BoolField:false)));
Queue.CompleteAdding();
await ConsumerTask;
/* Process stuff further */
}
}
struct Query
{
public readonly string[] WebPage;
public readonly bool BoolField;
public Query(uint e, IEnumerable<string> page, bool b) : this()
{
Webpage = page.ToArray();
BoolField = b;
}
}
CodesInChaos has spotted the problem in comments. It stems from having async methods returning void, which you should almost never do - it means you've got no way to track them.
Instead, if your async methods don't have any actual value to return, you should just make them return Task.
What's happening is that ParseSpecific is only running synchronously until the first await QueryWebPage(v) that doesn't complete immediately. It's then returning... so the task started here:
await Task.Run(() => _handler.ParseSpecific(mUrls));
... completes immediately, and "Done" gets printed.
Once you've made all your async methods return Task, you can await them. You also won't need Task.Run at all. So you'd have:
public async void DoWork()
{
RequestHandler _handler = new RequestHandler();
string[] mUrls;
await _handler.ParseSpecific(mUrls);
Console.WriteLine("Done...");
}
...
public async TaskParseSpecific(string[] urls)
{
foreach(string v in urls)
{
// Refactored for readability, although I'm not sure it really
// makes sense now that it's clearer! Are you sure this is what
// you want?
var page = await QueryWebPage(v);
Queue.Add(new Query(page, false);
}
Queue.CompleteAdding();
await ConsumerTask;
await ParseAll(true);
}
Your Reinit method also needs changing, as currently the ConsumerTask will basically complete almost immediately, as Consume will return immediately as it's another async method returning void.
To be honest, what you've got looks very complex, without a proper understanding of async/await. I would read up more on async/await and then probably start from scratch. I strongly suspect you can make this much, much simpler. You might also want to read up on TPL Dataflow which is designed to make producer/consumer scenarios simpler.

Categories