Wrapping a Task to return a value - c#

I feel like there is a better way to do the following and looking for other opinions.
Essentially, I am trying to utilize the async/await pattern but need to return a bool value from method to indicate what happened in the method which is causing me to wrap the Task with Task so this can be accomplished. My spidey-sense is telling me something is wrong with my implementation.
In the below example "LongRunningTask" is something I don't have control over because it is a library method that returns a Task, so something I can't change. The remainder of the flow logic could be changed. Obviously, this is just a fictional representation of my real-issue but meant to demonstrate the issue, so don't get caught-up in the hard-coded "GetData", etc...
Take a look and let me know of other options.
void Main()
{
StartApplication();
}
private async void StartApplication()
{
// This is a just fictional example passing hard-coded GetData
var didExecuteLongRunningTask = await ProcessStuff("GetData");
if (didExecuteLongRunningTask)
{
Console.WriteLine("Long running task was executed");
}
else {
Console.WriteLine("Long running task was NOT executed");
}
}
// Define other methods and classes here
private async Task<bool> ProcessStuff(string command)
{
if (command == "GetData")
{
await LongRunningTask();
return await Task<bool>.Factory.StartNew(() => true);
}
else
{
return await Task<bool>.Factory.StartNew(() => false);
}
}
private Task LongRunningTask()
{
return Task.Delay(2000);
}

Yes, you are right, you are over-complicating it. You can just do:
private async Task<bool> ProcessStuff(string command)
{
if (command == "GetData")
{
await LongRunningTask();
return true;
}
else
{
return false;
}
}
You can look at the MSDN for more information: Asynchronous Programming

Related

How to pause task running on a worker thread and wait for user input?

If I have a task running on a worker thread and when it finds something wrong, is it possible to pause and wait for the user to intervene before continuing?
For example, suppose I have something like this:
async void btnStartTask_Click(object sender, EventArgs e)
{
await Task.Run(() => LongRunningTask());
}
// CPU-bound
bool LongRunningTask()
{
// Establish some connection here.
// Do some work here.
List<Foo> incorrectValues = GetIncorrectValuesFromAbove();
if (incorrectValues.Count > 0)
{
// Here, I want to present the "incorrect values" to the user (on the UI thread)
// and let them select whether to modify a value, ignore it, or abort.
var confirmedValues = WaitForUserInput(incorrectValues);
}
// Continue processing.
}
Is it possible to substitute WaitForUserInput() with something that runs on the UI thread, waits for the user's intervention, and then acts accordingly? If so, how? I'm not looking for complete code or anything; if someone could point me in the right direction, I would be grateful.
What you're looking for is almost exactly Progress<T>, except you want to have the thing that reports progress get a task back with some information that they can await and inspect the results of. Creating Progress<T> yourself isn't terribly hard., and you can reasonably easily adapt it so that it computes a result.
public interface IPrompt<TResult, TInput>
{
Task<TResult> Prompt(TInput input);
}
public class Prompt<TResult, TInput> : IPrompt<TResult, TInput>
{
private SynchronizationContext context;
private Func<TInput, Task<TResult>> prompt;
public Prompt(Func<TInput, Task<TResult>> prompt)
{
context = SynchronizationContext.Current ?? new SynchronizationContext();
this.prompt += prompt;
}
Task<TResult> IPrompt<TResult, TInput>.Prompt(TInput input)
{
var tcs = new TaskCompletionSource<TResult>();
context.Post(data => prompt((TInput)data)
.ContinueWith(task =>
{
if (task.IsCanceled)
tcs.TrySetCanceled();
if (task.IsFaulted)
tcs.TrySetException(task.Exception.InnerExceptions);
else
tcs.TrySetResult(task.Result);
}), input);
return tcs.Task;
}
}
Now you simply need to have an asynchronous method that accepts the data from the long running process and returns a task with whatever the user interface's response is.
You can use TaskCompletionSource to generate a task that can be awaited within the LongRunningTask.
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace ConsoleApp5
{
class Program
{
private static event Action<string> Input;
public static async Task Main(string[] args)
{
var inputTask = InputTask();
var longRunningTask = Task.Run(() => LongRunningTask());
await Task.WhenAll(inputTask, longRunningTask);
}
private static async Task InputTask()
{
await Task.Yield();
while(true)
{
var input = await Console.In.ReadLineAsync();
Input?.Invoke(input);
}
}
static async Task<bool> LongRunningTask()
{
SomeExpensiveCall();
var incorrectValues = GetIncorrectValuesFromAbove();
if (incorrectValues.Count > 0)
{
var confirmedValues = await WaitForUserInput(incorrectValues).ConfigureAwait(false);
}
// Continue processing.
return true;
}
private static void SomeExpensiveCall()
{
}
private static Task<string> WaitForUserInput(IList<string> incorrectValues)
{
var taskCompletionSource = new TaskCompletionSource<string>();
Console.Write("Input Data: ");
try
{
void EventHandler(string input)
{
Input -= EventHandler;
taskCompletionSource.TrySetResult(input);
}
Input += EventHandler;
}
catch(Exception e)
{
taskCompletionSource.TrySetException(e);
}
return taskCompletionSource.Task;
}
private static IList<string> GetIncorrectValuesFromAbove()
{
return new List<string> { "Test" };
}
}
}
Of course in this example you could have just called await Console.In.ReadLineAsync() directly, but this code is to simulate an environment where you only have an event based API.
There are several ways to solve this problem, with the Control.Invoke being probably the most familiar. Here is a more TPL-ish approach. You start by declaring a UI related scheduler as a class field:
private TaskScheduler _uiScheduler;
Then initialize it:
public MyForm()
{
InitializeComponent();
_uiScheduler = TaskScheduler.FromCurrentSynchronizationContext();
}
Then you convert your synchronous LongRunning method to an asynchronous method. This means that it must return Task<bool> instead of bool. It must also have the async modifier, and by convention be named with the Async suffix:
async Task<bool> LongRunningAsync()
Finally you use the await operator in order to wait for the user's input, which will be a Task configured to run on the captured UI scheduler:
async Task<bool> LongRunningAsync()
{
// Establish some connection here.
// Do some work here.
List<Foo> incorrectValues = GetIncorrectValuesFromAbove();
if (incorrectValues.Count > 0)
{
// Here, I want to present the "incorrect values" to the user (on the UI thread)
// and let them select whether to modify a value, ignore it, or abort.
var confirmedValues = await Task.Factory.StartNew(() =>
{
return WaitForUserInput(incorrectValues);
}, default, TaskCreationOptions.None, _uiScheduler);
}
// Continue processing.
}
Starting the long running task is the same as before. The Task.Run understands async delegates, so you don't have to do something special after making the method async.
var longRunningTask = Task.Run(() => LongRunningAsync());
This should be enough, provided that you just intend to show a dialog box to the user. The Form.ShowDialog is a blocking method, so the WaitForUserInput method needs not to be asynchronous. If you had to allow the user to interact freely with the main form, the problem would be much more difficult to solve.
Another example using Invoke() and a ManualResetEvent. Let me know if you need help with the form code; setting up a constructor, using DialogResult, or creating a property to hold the "confirmedValues":
bool LongRunningTask()
{
// Establish some connection here.
// Do some work here.
List<Foo> incorrectValues = GetIncorrectValuesFromAbove();
var confirmedValues;
if (incorrectValues.Count > 0)
{
DialogResult result;
ManualResetEvent mre = new ManualResetEvent(false);
this.Invoke((MethodInvoker)delegate
{
// pass in incorrectValues to the form
// you'll have to build a constructor in it to accept them
frmSomeForm frm = new frmSomeForm(incorrectValues);
result = frm.ShowDialog();
if (result == DialogResult.OK)
{
confirmedValues = frm.confirmedValues; // get the confirmed values somehow
}
mre.Set(); // release the block below
});
mre.WaitOne(); // blocks until "mre" is set
}
// Continue processing.
}

How to use action delegate to avoid if else

I have the following code:
public class NotificationService {
private readonly Dictionary<NotificationMessageType, Action<IList<RecipientDetail>, NotificationMessageType>> _actionMap;
public NotificationService() [
_actionMap = new Dictionary<NotificationMessageType, Action<IList<RecipientDetail>, NotificationMessageType>> ();
_actionMap.Add(NotificationMessageType.SessionBookedReminder, new Action<IList<RecipientDetail>, NotificationMessageType>(GenerateNotificationsAsync)); //getting errror here because of async await
}
public async Task GenerateNotificationsAsync(IList<RecipientDetail> usersToNotify, NotificationMessageType messageType)
{
Would like to avoid if else here:
if(messageType == NotificationMessageType.SessionBookedReminder)
{
await Task.WhenAll(usersToNotify.Select(u => GenerateBookingNotificationAsync(u, messageType)).ToArray());
}
else
{
await Task.WhenAll(usersToNotify.Select(u => SendNotificationAsync(u, messageType)).ToArray());
}
}
public async Task GenerateNotificationsAsync(IList<RecipientDetail> usersToNotify, NotificationMessageType messageType)
{
}
public async Task GenerateBookingNotificationAsync(RecipientDetail userToNotify, NotificationMessageType messageType)
{
}
}
How can I use action delegate to avoid if else. I've tried with following, but getting error due to async await.
Can anyone help how to do the right way?
Thanks
Your dictionary must match the method declaration, your methods returns a task, so your
Action<IList<RecipientDetail>, NotificationMessageType>
Must be changed to something like
Func<IList<RecipientDetail>, NotificationMessageType,Task>
your method must return a task in order to use it in a async/await manner

Close task before run again

I working on real-time search. At this moment on property setter which is bounded to edit text, I call a method which calls API and then fills the list with the result it looks like this:
private string searchPhrase;
public string SearchPhrase
{
get => searchPhrase;
set
{
SetProperty(ref searchPhrase, value);
RunOnMainThread(SearchResult.Clear);
isAllFriends = false;
currentPage = 0;
RunInAsync(LoadData);
}
}
private async Task LoadData()
{
var response = await connectionRepository.GetConnections(currentPage,
pageSize, searchPhrase);
foreach (UserConnection uc in response)
{
if (uc.Type != UserConnection.TypeEnum.Awaiting)
{
RunOnMainThread(() =>
SearchResult.Add(new ConnectionUser(uc)));
}
}
}
But this way is totally useless because of it totally mashup list of a result if a text is entering quickly. So to prevent this I want to run this method async in a property but if a property is changed again I want to kill the previous Task and star it again. How can I achieve this?
Some informations from this thread:
create a CancellationTokenSource
var ctc = new CancellationTokenSource();
create a method doing the async work
private static Task ExecuteLongCancellableMethod(CancellationToken token)
{
return Task.Run(() =>
{
token.ThrowIfCancellationRequested();
// more code here
// check again if this task is canceled
token.ThrowIfCancellationRequested();
// more code
}
}
It is important to have this checks for cancel in the code.
Execute the function:
var cancellable = ExecuteLongCancellableMethod(ctc.Token);
To stop the long running execution use
ctc.Cancel();
For further details please consult the linked thread.
This question can be answered in many different ways. However IMO I would look at creating a class that
Delays itself automatically for X (ms) before performing the seach
Has the ability to be cancelled at any time as the search request changes.
Realistically this will change your code design, and should encapsulate the logic for both 1 & 2 in a separate class.
My initial thoughts are (and none of this is tested and mostly pseudo code).
class ConnectionSearch
{
public ConnectionSearch(string phrase, Action<object> addAction)
{
_searchPhrase = phrase;
_addAction = addAction;
_cancelSource = new CancellationTokenSource();
}
readonly string _searchPhrase = null;
readonly Action<object> _addAction;
readonly CancellationTokenSource _cancelSource;
public void Cancel()
{
_cancelSource?.Cancel();
}
public async void PerformSearch()
{
await Task.Delay(300); //await 300ms between keystrokes
if (_cancelSource.IsCancellationRequested)
return;
//continue your code keep checking for
//loop your dataset
//call _addAction?.Invoke(uc);
}
}
This is basic, really just encapsulates the logic for both points 1 & 2, you will need to adapt the code to do the search.
Next you could change your property to cancel a previous running instance, and then start another instance immediatly after something like below.
ConnectionSearch connectionSearch;
string searchPhrase;
public string SearchPhrase
{
get => searchPhrase;
set
{
//do your setter work
if(connectionSearch != null)
{
connectionSearch.Cancel();
}
connectionSearch = new ConnectionSearch(value, addConnectionUser);
connectionSearch.PerformSearch();
}
}
void addConnectionUser(object uc)
{
//pperform your add logic..
}
The code is pretty straight forward, however you will see in the setter is simply cancelling an existing request and then creating a new request. You could put some disposal cleanup logic in place but this should get you started.
You can implement some sort of debouncer which will encapsulate the logics of task result debouncing, i.e. it will assure if you run many tasks, then only the latest task result will be used:
public class TaskDebouncer<TResult>
{
public delegate void TaskDebouncerHandler(TResult result, object sender);
public event TaskDebouncerHandler OnCompleted;
public event TaskDebouncerHandler OnDebounced;
private Task _lastTask;
private object _lock = new object();
public void Run(Task<TResult> task)
{
lock (_lock)
{
_lastTask = task;
}
task.ContinueWith(t =>
{
if (t.IsFaulted)
throw t.Exception;
lock (_lock)
{
if (_lastTask == task)
{
OnCompleted?.Invoke(t.Result, this);
}
else
{
OnDebounced?.Invoke(t.Result, this);
}
}
});
}
public async Task WaitLast()
{
await _lastTask;
}
}
Then, you can just do:
private readonly TaskDebouncer<Connections[]> _connectionsDebouncer = new TaskDebouncer<Connections[]>();
public ClassName()
{
_connectionsDebouncer.OnCompleted += OnConnectionUpdate;
}
public void OnConnectionUpdate(Connections[] connections, object sender)
{
RunOnMainThread(SearchResult.Clear);
isAllFriends = false;
currentPage = 0;
foreach (var conn in connections)
RunOnMainThread(() => SearchResult.Add(new ConnectionUser(conn)));
}
private string searchPhrase;
public string SearchPhrase
{
get => searchPhrase;
set
{
SetProperty(ref searchPhrase, value);
_connectionsDebouncer.Add(RunInAsync(LoadData));
}
}
private async Task<Connection[]> LoadData()
{
return await connectionRepository
.GetConnections(currentPage, pageSize, searchPhrase)
.Where(conn => conn.Type != UserConnection.TypeEnum.Awaiting)
.ToArray();
}
It is not pretty clear what RunInAsync and RunOnMainThread methods are.
I guess, you don't actually need them.

How to best prevent running async method again before it completes?

I've got this pattern for preventing calling into an async method before it has had a chance to complete previously.
My solution involving needing a flag, and then needing to lock around the flag, feels pretty verbose. Is there a more natural way of achieving this?
public class MyClass
{
private object SyncIsFooRunning = new object();
private bool IsFooRunning { get; set;}
public async Task FooAsync()
{
try
{
lock(SyncIsFooRunning)
{
if(IsFooRunning)
return;
IsFooRunning = true;
}
// Use a semaphore to enforce maximum number of Tasks which are able to run concurrently.
var semaphoreSlim = new SemaphoreSlim(5);
var trackedTasks = new List<Task>();
for(int i = 0; i < 100; i++)
{
await semaphoreSlim.WaitAsync();
trackedTasks.Add(Task.Run(() =>
{
// DoTask();
semaphoreSlim.Release();
}));
}
// Using await makes try/catch/finally possible.
await Task.WhenAll(trackedTasks);
}
finally
{
lock(SyncIsFooRunning)
{
IsFooRunning = false;
}
}
}
}
As noted in the comments, you can use Interlocked.CompareExchange() if you prefer:
public class MyClass
{
private int _flag;
public async Task FooAsync()
{
try
{
if (Interlocked.CompareExchange(ref _flag, 1, 0) == 1)
{
return;
}
// do stuff
}
finally
{
Interlocked.Exchange(ref _flag, 0);
}
}
}
That said, I think it's overkill. Nothing wrong with using lock in this type of scenario, especially if you don't expect a lot of contention on the method. What I do think would be better is to wrap the method so that the caller can always await on the result, whether a new asynchronous operation was started or not:
public class MyClass
{
private readonly object _lock = new object();
private Task _task;
public Task FooAsync()
{
lock (_lock)
{
return _task != null ? _task : (_task = FooAsyncImpl());
}
}
public async Task FooAsyncImpl()
{
try
{
// do async stuff
}
finally
{
lock (_lock) _task = null;
}
}
}
Finally, in the comments, you say this:
Seems a bit odd that all the return types are still valid for Task?
Not clear to me what you mean by that. In your method, the only valid return types would be void and Task. If your return statement(s) returned an actual value, you'd have to use Task<T> where T is the type returned by the return statement(s).

What is the Task equivalent to Promise.then()?

With the addition of async / await to TypeScript using Promise(s) can look very syntactically close to Task(s).
Example:
Promise (TS)
public async myAsyncFun(): Promise<T> {
let value: T = await ...
return value;
}
Task (C#)
public async Task<T> MyAsyncFun() {
T value = await ...
return value;
}
I was wondering if the other way around, there was an equivalent to .then() for Task(s).
Example:
Promise (TS)
Promise<T> promise = ...
promise.then((result: T) => ...do something...);
I've used ContinueWith which can work if you have one or multiple Tasks running.
example:
public async Task<T> MyAsyncFun() {
T value = await ...
return value;
}
MyAsyncFun().ContinueWith(...
https://msdn.microsoft.com/en-us/library/dd270696(v=vs.110).aspx
You can create a handy extension method for task continuations to match the javascript .then function. I find it easier to use than .ContinueWith, which has its own set of pitfalls and encumbrances.
public static class TaskExtensions
{
public static async Task<TV> Then<T,TV>(this Task<T> task, Func<T,TV> then)
{
var result = await task;
return then(result);
}
}
Then you can use it like so
Task<User> userRecord = GetUserById(123)
Task<string> firstName = userRecord.Then(u => u.FirstName)

Categories

Resources