I have a function :
public async Task DoStuff()
{
await DoSomethingAsync();
}
I need to call it from code running this:
list.ItemTapped += async (sender, e) =>
{
file.WriteAllBytes(reportPdf, data, () => **await** file.DoStuff());
};
But the await is not allowed in the Action file.DoStuff called in the above???
While GazTheDestroyer's answer is correct, there's no reason for the lambda expression to be marked async and to use an await since file.DoStuff already returns a Task and fits the Func<Task> delegate:
list.ItemTapped += async (sender, e) =>
{
file.WriteAllBytes(reportPdf, data, () => file.DoStuff());
};
It would also slightly improve performance since there's no reason to construct the state machine required for an async-await method/delegate.
Your second lambda is not marked async
list.ItemTapped += async (sender, e) =>
{
file.WriteAllBytes(reportPdf, data, async () => await file.DoStuff());
};
This depends on file.WriteAllBytes() allowing a Func<Task> to be passed in. An Action will not be enough.
Related
The following scenario runs under .NET 4.5 so any UnobservedTaskException does not terminate the process.
I have the habit of listening to any UnobservedTaskException thrown by having this execute at the start of my app:
private void WatchForUnobservedTaskExceptions()
{
TaskScheduler.UnobservedTaskException += (sender, args) =>
{
args.Exception.Dump("Ooops");
};
}
I also have a helper method for when I want to explicitly ignore any exceptions thrown by my tasks:
public static Task IgnoreExceptions(Task task)
=> task.ContinueWith(t =>
{
var ignored = t.Exception.Dump("Checked");
},
CancellationToken.None,
TaskContinuationOptions.ExecuteSynchronously,
TaskScheduler.Default);
So if I have the following code execute:
void Main()
{
WatchForUnobservedTaskExceptions();
var task = Task.Factory.StartNew(() =>
{
Thread.Sleep(1000);
throw new InvalidOperationException();
});
IgnoreExceptions(task);
GC.Collect(2);
GC.WaitForPendingFinalizers();
Console.ReadLine();
}
After we return from the Console.ReadLine() we will not see any UnobservedTaskException thrown which is what we expect.
However if I change the above task to start using async/await with everything else the same as before:
var task = Task.Factory.StartNew(async () =>
{
await Task.Delay(1000);
throw new InvalidOperationException();
});
Now we get the UnobservedTaskException thrown. Debugging the code reveals the continuation executes with the t.Exception being null.
How can I ignore the exceptions properly in both scenarios?
Either use
var task = Task.Factory.StartNew(async () =>
{
await Task.Delay(1000);
throw new InvalidOperationException();
}).Unwrap();
or
var task = Task.Run(async () =>
{
await Task.Delay(1000);
throw new InvalidOperationException();
});
See this blogpost about Task.Run vs Task.Factory.StartNew about using Task.Factory.StartNew with async modifiers
By using the async keyword here, the compiler is going to map this delegate to be a Func<Task<int>>: invoking the delegate will return the Task<int> to represent the eventual completion of this call. And since the delegate is Func<Task<int>>, TResult is Task<int>, and thus the type of ‘t’ is going to be Task<Task<int>>, not Task<int>.
To handle these kinds of cases, in .NET 4 we introduced the Unwrap method.
Some more background
Why Not to Use Task.Factory.StartNew?
.. Does not understand async delegates. … . The problem is that when you pass an async delegate to StartNew, it’s natural to assume that the returned task represents that delegate. However, since StartNew does not understand async delegates, what that task actually represents is just the beginning of that delegate. This is one of the first pitfalls that coders encounter when using StartNew in async code.
EDIT
The type of task in var task = Task.Factory.StartNew(async (...)) => is actually Task<Task<int>>. You have to Unwrap that to get the source task. With that in mind:
You can only call Unwrap on a Task<Task>> so you could add an overload to IgnoreExceptions to accommodate that:
void Main()
{
WatchForUnobservedTaskExceptions();
var task = Task.Factory.StartNew(async () =>
{
await Task.Delay(1000);
throw new InvalidOperationException();
});
IgnoreExceptions(task);
GC.Collect(2);
GC.WaitForPendingFinalizers();
Console.ReadLine();
}
private void WatchForUnobservedTaskExceptions()
{
TaskScheduler.UnobservedTaskException += (sender, args) =>
{
args.Exception.Dump("Ooops");
};
}
public static Task IgnoreExceptions(Task task)
=> task.ContinueWith(t =>
{
var ignored = t.Exception.Dump("Checked");
},
CancellationToken.None,
TaskContinuationOptions.ExecuteSynchronously,
TaskScheduler.Default);
public static Task IgnoreExceptions(Task<Task> task)
=> task.Unwrap().ContinueWith(t =>
{
var ignored = t.Exception.Dump("Checked");
},
CancellationToken.None,
TaskContinuationOptions.ExecuteSynchronously,
TaskScheduler.Default);
Combination of var and Task and Task<T>'s interited relationship, masks the problem. If I rewrite the code slightly, it will become obvious where the problem is.
Task<int> task1 = Task.Factory.StartNew(() =>
{
Thread.Sleep(1000);
throw new InvalidOperationException();
return 1;
});
Task<Task<int>> task2 = Task.Factory.StartNew(async () =>
{
await Task.Delay(1000);
throw new InvalidOperationException();
return 1;
});
This better illustrates what Peter Bons is talking about.
I am writing a windows store app and needs some help on Task.Run method. I am calling service to retrieve data from a service; because I want to cancel the task when internet is disconnected I am passing CancellationToken
await Task.Run(async () => await Download1(), cts.Token);
There is another download method which should run after this above task is finished so I am using await. Both the tasks write to same files so I want to make sure that they do not run in parallel.
await Task.Run(async () => await Download2(), cts.Token);
The issue is that above task 2 starts without task 1 is finished and so both task run in parallel. What I am doing wrong? Please advise.
Download1 looks like this:-
public async Task Download1()
{
await Windows.ApplicationModel.Core.CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(CoreDispatcherPriority.Normal,
async () =>
{
Status = "Downloading!";
var ListSetupTasks = new List<SetupView>();
foreach (var setup in AllSetupTasks.Setup)
{
ListSetupTasks.Add(new SetupViewModel(setup));
}
IEnumerable<Task> downloadTasksQuery = from setup in ListSetupTasks where setup.TaskType == TaskType.Web select _masterlistrepository.GetTask(setup, false, datetime, branch);
Task[] downloadTasks = downloadTasksQuery.ToArray();
await Task.WhenAll(downloadTasks);
IEnumerable<Task> FinalTasksQuery = from setup in ListSetupTasks where setup.TaskType == TaskType.Web select _masterlistrepository.GetTask(setup, false);
foreach (var task in FinalTasksQuery)
{
await task;
}
});
}
The CancellationToken is used like this:-
async void NetworkChanged(object sender, NetworkConnectionStatusChangedEventArgs e)
{
if (e.Value == false && LoadingData == true)
{
await Windows.ApplicationModel.Core.CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(CoreDispatcherPriority.Normal,
async () =>
{
await _alertmessageservice.ShowAsync("", "Network error. Please retry!");
cts.Cancel();
});
}
}
You use the CoreDispatcher.RunAsync which accepts a DispatchedHandler. DispatchedHandler has this signature:
public delegate void DispatchedHandler()
So when you pass in an async lambda it will end up being async void. That makes it impossible to await as there's no task being returned. This means that everything after an await in your lambdas will run on a different thread concurrently.
You shouldn't be passing an async delegate to that method.
As a side note, passing a CancellationToken doesn't enable to automatically stop the operation. It can only stop the operation before it starts or if you're observing the token somewhere inside the operation.
So, to actually use the CancellationToken Download should accept use it which makes the Task.Run redundant:
await Download1Async(cts.Token);
Can you just call them sequentially?
await Task.Run(async () =>
{
await Download1();
await Download2();
});
You might not even need Task.Run, unless you are calling from a UI thread. If you're already on a background thread, then try just:
await Download1();
await Download2();
Of course, your cancellation token should probably be passed into the download function either way.
I am working in C# on .NET 4.0 and have started replacing a number of nested BackgroundWorker setups with Task<T>.
The "nesting" is of this form:
var secondWorker = new BackgroundWorker();
secondWorker.DoWork += (sender, args) =>
{
MoreThings();
};
var firstWorker = new BackgroundWorker();
firstWorker.DoWork += (sender, args) =>
{
args.Result = this.Things();
};
firstWorker.RunWorkerCompleted += (sender, args) =>
{
var result = (bool)args.Result;
// possibly do things on UI
if (result) { secondWorker.RunWorkerAsync(); }
};
secondWorker here plays the role of a callback for firstWorker. The equivalent when using Task<T>, as I understand it, are continuations with ContinueWith(); however, that doesn't allow me to decide whether to actually run the continuation from the control flow in a particular case.
A - from my understanding very unclean - workaround would be this:
var source = new CancellationTokenSource();
var uiScheduler = TaskScheduler.FromCurrentSynchronizationContext();
Task.Factory.StartNew(() => { return this.Things(); })
.ContinueWith(t =>
{
// do things on UI
if (!t.Result) { source.Cancel(); }
}, CancellationToken.None, TaskContinuationOptions.NotOnFaulted, uiScheduler)
.ContinueWith(t => { this.MoreThings(); }, source.Token);
This kind of works, but from all the examples I've seen, in this form (accessing the CancellationTokenSource from within the continuation chain - although the task that does is not using the token) it rather looks like abuse of the CancellationToken mechanism. How bad is this really? What would be the proper, "idiomatic" way to cancel the continuation chain based on information determined inside its flow?
(This code on the outside has the intended effect, but I assume it is the wrong way to solve the task with respect to using the existing tools. I am not looking for critique of my "solution" but for the proper way to do it. That's why I am putting this on SO rather than Code Review.)
With continuations, C# also provides the async method feature. In this scheme, your code would look something like this:
async Task<bool> Things() { ... }
async Task MoreThings() { ... }
async Task RunStuff()
{
if (await Things())
{
await MoreThings();
}
}
The exact specifics depend on your implementation details. The important thing here is that via async and await, C# will automatically generate a state machine that will abstract all the tedium away from dealing with continuations, in a way that makes it easy to organize them.
EDIT: it occurred to me that your actual Things() and MoreThings() methods, you may not want to actually convert to async, so you can do this instead:
async Task RunStuff()
{
if (await Task.Run(() => Things()))
{
await Task.Run(() => MoreThings());
}
}
That will wrap your specific methods in an asynchronous task.
EDIT 2: it having been pointed out to me that I overlooked the pre-4.5 restriction here, the following should work:
void RunStuff()
{
Task.Factory.StartNew(() => Things()).ContinueWith(task =>
{
if (task.Result)
{
Task.Factory.StartNew(() => MoreThings());
}
});
}
Something like that, anyway.
I want to run a method on background when app is closed.
async private void()
{
var bgw = new BackgroundWorker();
bgw.DoWork += (s,v) =>
{
string temp= await doSomething();
};
bgw.RunWorkerCompleted += (s,v) =>
{
//...
};
bgw.RunWorkerAsync();
}
It produces the following error
the await operator can only be used within an async lambda expression.
How do I fix it?
DoWork cannot be async. I recommend you use Task.Run instead:
async private void X()
{
await Task.Run(() => doSomething());
//...
}
However, you may need to call Wait instead of await since this is done at application shutdown.
I have a method HandleAcceptedConnection that is under Task.Run() that i want to run asynchronously(in another separate thread). I tried declaring HandleAcceptedConnection as async method and dont call await but it doesnt seem to run asynchronously. I can confirm that I can have Task.Run()(by watching the thread id) under another Task.Run() but is that recommended?
private async void Start_Click(object sender, RoutedEventArgs e)
{
var task = Task.Run(() =>
{
while (isContinue)
{
var handler = listener.Accept();
// handle connection
Log("Before");
Log("ThreadId Accept " + Thread.CurrentThread.ManagedThreadId);
// i want to run method below asynchronously. i want to
// wrap it under Task.Run() but i am already under
// Task.Run(). i set HandleAcceptedConnection as async. i thought by not
// calling await on HandleAcceptedConnection, HandleAcceptedConnection
// is asynchronous
HandleAcceptedConnection(handler);
Log("After");
isContinue = true;
}
});
await task;
}
private async Task HandleAcceptedConnection(Socket handler)
{
Log("ThreadId HandleAcceptedConnection " + Thread.CurrentThread.ManagedThreadId);
Log("Under HandleAcceptedConnection");
Thread.Sleep(10000);
}
When i run this, logs says
Before
Under HandleAcceptedConnection
After
i want
Before
After
Under HandleAcceptedConnection
i want HandleAcceptedConnection to be run asynchronously. Should i wrap it under another Task.Run or it is already asynchronous?
Did you try
private async Task HandleAcceptedConnection(Socket handler)
{
Thread.Sleep(1000);
Log("Under HandleAcceptedConnection");
}
Because doing something on another thread doesn't mean it'll be delayed.
You should be using AcceptTcpClientAsync, then you won't need extra threads. Check this answer for an example. Don't use a synchronous API when there is a naturally asynchronous version of it available.
Updated to address the comment. Nothing prevents you from using Task.Run from inside Task.Run, you code might look like this (untested):
private async void Start_Click(object sender, RoutedEventArgs e)
{
var connectionTasks = new List<Task>();
Func<Task> handleConnection = async () =>
{
var connectionTask = Task.Run(() => HandleAcceptedConnection(handler));
connectionTasks.Add(connectionTask);
await connectionTask;
connectionTasks.Remove(connectionTask);
};
var task = Task.Run(() =>
{
while (isContinue)
{
var handler = listener.Accept();
// handle connection
Log("Before");
Log("ThreadId Accept " + Thread.CurrentThread.ManagedThreadId);
var connectionTask = handleConnection();
Log("After");
isContinue = true;
}
});
await task;
}