Supposed I have to get login information from the server then login, then after the login do something next. The goal is to encapsulate all of this into something that caller can wait.
However each of Firebase C# API call already returned separate tasks (that has already been started). My approach is to think of how to chain each of these separate task together.
I am thinking of making a new Task that represent this since it also seems great for aggregating all the exceptions for the caller to catch.
We have
Task<DataSnapshot> getKeyTask = Database.Child("keyLocation").GetValueAsync();
GetValueAsync as I understand already spun up the task. We can observe the returned task or better use continuation to begin something when it finished.
Task loginTask = Auth.SignInWithEmailAndPasswordAsync(username, password);
Auth.SignInWithEmailAndPasswordAsync also spun up a new task in itself. I can also use continuation to wait and begin something else.
After the login I would like to use another database call to get user data.
So
public Task Login()
{
Task<DataSnapshot> getKeyTask = Database.Child("keyLocation").GetValueAsync();
Task loginAfterGetKeyTask = getKeyTask.ContinueWith(gkt =>
{
Task loginTask = Auth.SignInWithEmailAndPasswordAsync(gkt.Result.___.username, gkt.Result.____.password);
loginTask.ContinueWith(lt => { ....the next task.... });
});
return loginAfterGetKeyTask;
}
There are 2 problems I found :
As it goes on the code is getting deeper in the pyramid. Is there any way I can do to make the code looks more sequential?
This Login() function I want to return a Task so that caller can wait for the whole login process to finish then continue, etc. But currently the returned loginAfterGetKey task finished even if there are more things to do inside. I could keep doing .ContinueWith chaining on the outside if not for that there are one more Firebase task that is executed. Ideally I would like to return the Task that represent the whole thing but they are nested inside. Firebase's method start those task on it's own so I think I could not try to bundle all the task on my own.
I tried encapsulate all the tasks as a new Task start from Task.Factory.StartNew however "GetComponent can only be called from main thread" is a problem. (The Auth and other Firebase service would require GetComponent)
I tried using IEnumerator as a return of Login() and planned to put yield in this function, however, "The yield statement cannot be used inside anonymous method or lambda expression".
I tried using loginTask.Wait() inside the first ContinueWith in order to make loginAfterGetKeyTask that I return from this function not arrive at IsCompleted state until it reacehs the end (which would have to wait for the tasks inside) but since the lambda in this ContinueWith is in the main thread, it causes deadlock.
In the end I used IEnumerator loop waiting as a kind of hack to wait for the Task to finish. It 'linearize' the Task multi-thread system into single thread coroutine system that Unity seems to prefer. Each yield return null results in resuming in the next frame to check on the Task's status again.
Not very elegant but I could not find any better way outside async/await that pairs with Task but Unity's C# version does not yet support it.
public void Caller()
{
yield return StartCoroutine(Login());
// Login finished, check the login credentials
}
private IEnumerator WaitTask(Task task)
{
while (task.IsCompleted == false)
{
yield return null;
}
if(task.IsFaulted)
{
throw task.Exception;
}
}
public IEnumerator Login()
{
Task<DataSnapshot> getKeyTask = Database.Child("keyLocation").GetValueAsync();
yield return WaitTask(getKeyTask);
Task loginTask = Auth.SignInWithEmailAndPasswordAsync(getKeyTask.Result.___.username, getKeyTask.Result.____.password);
yield return WaitTask(loginTask);
//... the next task can use loginTask.Result etc.
}
It would be tricky still to catch exception since compiler does not allow try catch to wrap the yield. One way I might use the callback pattern to send the exception to the caller.
Here's an extension version in case you want to do like yield return task.YieldWait(); instead.
public static class TaskExtension
{
/// <summary>
/// Firebase Task might not play well with Unity's Coroutine workflow. You can now yield on the task with this.
/// </summary>
public static IEnumerator YieldWait(this Task task)
{
while (task.IsCompleted == false)
{
yield return null;
}
if(task.IsFaulted)
{
throw task.Exception;
}
}
}
Related
I'm developing an application for monitoring certain tasks (e.g. if certain services/websites are currently up and running, certain records in the database exist, etc.). And as most these tasks are long running, I use TPL with async/await.
I have an base class for all such tasks:
public abstract class LongRunningOperation
{
// .. some props...
internal async void Start()
{
try
{
this.Status = OperationStatus.Started;
await this.DoStart();
this.Status = OperationStatus.Finished;
}
catch (Exception e)
{
this.Status = OperationStatus.Error;
this.Message = e.ToString();
}
}
protected abstract Task DoStart();
}
And method that launches these tasks looks like this:
public static LongRunningOperation[] LaunchOperations()
{
LongRunningOperation[] operations = GetAllLongRunningOperations();
foreach (var o in operations)
Task.Factory.StartNew(() => { o.Start(); });
return operations;
}
The array returned by this method is used to monitor all LongRunningOperations and log the stats. currently I have a console application having a while (true) loop that prints out the stats (name, status, current runtime) for each operation on the screen (refreshing every second) until all the operations are finished.
The thing that bothers me is the async void method. I've read that it's bad practice to use async void methods, but:
I can't figure out what harm they might do in my scenario
If I change the Start method to return Task, its return value will never be used anywhere, and I can't think why I need it
I'd appreciate it if someone could clarify these points
An async void method is a "fire and forget" operation. You can not wait for any result, and will not know when the operation completes and if it has been successful or not.
Basically you should use void when you are sure that you'll never need to know when the operation finished and if the operation execution was successful or not (for example writing logs).
With async methods that return Task, a caller is capable of waiting for an operation to finish, and also handle exceptions that happened during the execution of the operation.
To summarize, if you do not need a result, an async Task is slightly better because you can await it as well as handle exceptions and deal with task ordering.
The best practice is to collect all the async calls in a collection inside the loop and do Task.WhenAll(). Yet, want to understand what happens when an await is encountered inside the loop, what would the returned Task contain? what about further async calls? Will it create new tasks and add them to the already returned Task sequentially?
As per the code below
private void CallLoopAsync()
{
var loopReturnedTask = LoopAsync();
}
private async Task LoopAsync()
{
int count = 0;
while(count < 5)
{
await SomeNetworkCallAsync();
count++;
}
}
The steps I assumed are
LoopAsync gets called
count is set to zero, code enters while loop, condition is checked
SomeNetworkCallAsync is called,and the returned task is awaited
New task/awaitable is created
New task is returned to CallLoopAsync()
Now, provided there is enough time for the process to live, How / In what way, will the next code lines like count++ and further SomeNetworkCallAsync be executed?
Update - Based on Jon Hanna and Stephen Cleary:
So there is one Task and the implementation of that Task will involve
5 calls to NetworkCallAsync, but the use of a state-machine means
those tasks need not be explicitly chained for this to work. This, for
example, allows it to decide whether to break the looping or not based
on the result of a task, and so on.
Though they are not chained, each call will wait for the previous call to complete as we have used await (in state m/c, awaiter.GetResult();). It behaves as if five consecutive calls have been made and they are executed one after the another (only after the previous call gets completed). If this is true, we have to be bit more careful in how we are composing the async calls.For ex:
Instead of writing
private async Task SomeWorkAsync()
{
await SomeIndependentNetworkCall();// 2 sec to complete
var result1 = await GetDataFromNetworkCallAsync(); // 2 sec to complete
await PostDataToNetworkAsync(result1); // 2 sec to complete
}
It should be written
private Task[] RefactoredSomeWorkAsync()
{
var task1 = SomeIndependentNetworkCall();// 2 sec to complete
var task2 = GetDataFromNetworkCallAsync()
.ContinueWith(result1 => PostDataToNetworkAsync(result1)).Unwrap();// 4 sec to complete
return new[] { task1, task2 };
}
So that we can say RefactoredSomeWorkAsync is faster by 2 seconds, because of the possibility of parallelism
private async Task CallRefactoredSomeWorkAsync()
{
await Task.WhenAll(RefactoredSomeWorkAsync());//Faster, 4 sec
await SomeWorkAsync(); // Slower, 6 sec
}
Is this correct? - Yes. Along with "async all the way", "Accumulate tasks all the way" is good practice. Similar discussion is here
When count is zero, new task will be created because of await and be returned
No. It will not. It will simply call the async method consequently, without storing or returning the result. The value in loopReturnedTask will store the Task of LoopAsync, not related to SomeNetworkCallAsync.
await SomeNetworkCallAsync(); // call, wait and forget the result
You may want to read the MSDN article on async\await.
To produce code similar to what async and await do, if those keywords didn't exist, would require code a bit like:
private struct LoopAsyncStateMachine : IAsyncStateMachine
{
public int _state;
public AsyncTaskMethodBuilder _builder;
public TestAsync _this;
public int _count;
private TaskAwaiter _awaiter;
void IAsyncStateMachine.MoveNext()
{
try
{
if (_state != 0)
{
_count = 0;
goto afterSetup;
}
TaskAwaiter awaiter = _awaiter;
_awaiter = default(TaskAwaiter);
_state = -1;
loopBack:
awaiter.GetResult();
awaiter = default(TaskAwaiter);
_count++;
afterSetup:
if (_count < 5)
{
awaiter = _this.SomeNetworkCallAsync().GetAwaiter();
if (!awaiter.IsCompleted)
{
_state = 0;
_awaiter = awaiter;
_builder.AwaitUnsafeOnCompleted<TaskAwaiter, TestAsync.LoopAsyncStateMachine>(ref awaiter, ref this);
return;
}
goto loopBack;
}
_state = -2;
_builder.SetResult();
}
catch (Exception exception)
{
_state = -2;
_builder.SetException(exception);
return;
}
}
[DebuggerHidden]
void IAsyncStateMachine.SetStateMachine(IAsyncStateMachine param0)
{
_builder.SetStateMachine(param0);
}
}
public Task LoopAsync()
{
LoopAsyncStateMachine stateMachine = new LoopAsyncStateMachine();
stateMachine._this = this;
AsyncTaskMethodBuilder builder = AsyncTaskMethodBuilder.Create();
stateMachine._builder = builder;
stateMachine._state = -1;
builder.Start(ref stateMachine);
return builder.Task;
}
(The above is based on what happens when you use async and await except that the result of that uses names that cannot be valid C# class or field names, along with some extra attributes. If its MoveNext() reminds you of an IEnumerator that's not entirely irrelevant, the mechanism by which await and async produce an IAsyncStateMachine to implement a Task is similar in many ways to how yield produces an IEnumerator<T>).
The result is a single Task which comes from AsyncTaskMethodBuilder and makes use of LoopAsyncStateMachine (which is close to the hidden struct that the async produces). Its MoveNext() method is first called upon the task being started. It will then use an awaiter on SomeNetworkCallAsync. If it is already completed it moves on to the next stage (increment count and so on), otherwise it stores the awaiter in a field. On subsequent uses it will be called because the SomeNetworkCallAsync() task has returned, and it will get the result (which is void in this case, but could be a value if values were returned). It then attempts further loops and again returns when it is waiting on a task that is not yet completed.
When it finally reaches a count of 5 it calls SetResult() on the builder, which sets the result of the Task that LoopAsync had returned.
So there is one Task and the implementation of that Task will involve 5 calls to NetworkCallAsync, but the use of a state-machine means those tasks need not be explicitly chained for this to work. This, for example, allows it to decide whether to break the looping or not based on the result of a task, and so on.
When an async method first yields at an await, it returns a Task (or Task<T>). This is not the task being observed by the await; it is a completely different task created by the async method. The async state machine controls the lifetime of that Task.
One way to think of it is to consider the returned Task as representing the method itself. The returned Task will only complete when the method completes. If the method returns a value, then that value is set as the result of the task. If the method throws an exception, then that exception is captured by the state machine and placed on that task.
So, there's no need for attaching continuations to the returned task. The returned task will not complete until the method is done.
How / In what way, will the next code lines like count++ and further SomeNetworkCallAsync be executed?
I do explain this in my async intro post. In summary, when a method awaits, it captures a "current context" (SynchronizationContext.Current unless it is null, in which case it uses TaskScheduler.Current). When the await completes, it resumes executing its async method within that context.
That's what technically happens; but in the vast majority of cases, this simply means:
If an async method starts on a UI thread, then it will resume on that same UI thread.
If an async method starts within an ASP.NET request context, then it will resume with that same request context (not necessarily on the same thread, though).
Otherwise, the async method resumes on a thread pool thread.
I have an async method that fetches some data from a database. This operation is fairly expensive, and takes a long time to complete. As a result, I'd like to cache the method's return value. However, it's possible that the async method will be called multiple times before its initial execution has a chance to return and save its result to the cache, resulting in multiple calls to this expensive operation.
To avoid this, I'm currently reusing a Task, like so:
public class DataAccess
{
private Task<MyData> _getDataTask;
public async Task<MyData> GetDataAsync()
{
if (_getDataTask == null)
{
_getDataTask = Task.Run(() => synchronousDataAccessMethod());
}
return await _getDataTask;
}
}
My thought is that the initial call to GetDataAsync will kick off the synchronousDataAccessMethod method in a Task, and any subsequent calls to this method before the Task has completed will simply await the already running Task, automatically avoiding calling synchronousDataAccessMethod more than once. Calls made to GetDataAsync after the private Task has completed will cause the Task to be awaited, which will immediately return the data from its initial execution.
This seems to be working, but I'm having some strange performance issues that I suspect may be tied to this approach. Specifically, awaiting _getDataTask after it has completed takes several seconds (and locks the UI thread), even though the synchronousDataAccessMethod call is not called.
Am I misusing async/await? Is there a hidden gotcha that I'm not seeing? Is there a better way to accomplish the desired behavior?
EDIT
Here's how I call this method:
var result = (await myDataAccessObject.GetDataAsync()).ToList();
Maybe it has something to do with the fact that the result is not immediately enumerated?
If you want to await it further up the call stack, I think you want this:
public class DataAccess
{
private Task<MyData> _getDataTask;
private readonly object lockObj = new Object();
public async Task<MyData> GetDataAsync()
{
lock(lockObj)
{
if (_getDataTask == null)
{
_getDataTask = Task.Run(() => synchronousDataAccessMethod());
}
}
return await _getDataTask;
}
}
Your original code has the potential for this happening:
Thread 1 sees that _getDataTask == null, and begins constructing the task
Thread 2 sees that _getDataTask == null, and begins constructing the task
Thread 1 finishes constructing the task, which starts, and Thread 1 waits on that task
Thread 2 finishes constructing a task, which starts, and Thread 2 waits on that task
You end up with two instances of the task running.
Use the lock function to prevent multiple calls to the database query section. Lock will make it thread safe so that once it has been cached all the other calls will use it instead of running to the database for fulfillment.
lock(StaticObject) // Create a static object so there is only one value defined for this routine
{
if(_getDataTask == null)
{
// Get data code here
}
return _getDataTask
}
Please rewrite your function as:
public Task<MyData> GetDataAsync()
{
if (_getDataTask == null)
{
_getDataTask = Task.Run(() => synchronousDataAccessMethod());
}
return _getDataTask;
}
This should not change at all the things that can be done with this function - you can still await on the returned task!
Please tell me if that changes anything.
Bit late to answer this but there is an open source library called LazyCache that will do this for you in two lines of code and it was recently updated to handle caching Tasks for just this sort of situation. It is also available on nuget.
Example:
Func<Task<List<MyData>>> cacheableAsyncFunc = () => myDataAccessObject.GetDataAsync();
var cachedData = await cache.GetOrAddAsync("myDataAccessObject.GetData", cacheableAsyncFunc);
return cachedData;
// Or instead just do it all in one line if you prefer
// return await cache.GetOrAddAsync("myDataAccessObject.GetData", myDataAccessObject.GetDataAsync);
}
It has built in locking by default so the cacheable method will only execute once per cache miss, and it uses a lamda so you can do "get or add" in one go. It defaults to 20 minutes sliding expiration but you can set whatever caching policy you like on it.
More info on caching tasks is in the api docs and you may find the sample app to demo caching tasks useful.
(Disclaimer: I am the author of LazyCache)
In Asp.Net MVC project I have an Asynchronus controller, wand I also have an action of type Task to implement Asynchronus functionality.
Well I have the following code,
Action startMethod = delegate() { CreateUserFunctionality(user.UserID); };
return Task.Factory.StartNew(startMethod).ContinueWith(
t =>
{
return (ActionResult)RedirectToAction("Profile", "UserProfile");
});
And I also has, CreateUserFunctionality as
public void CreateUserFunctionality(int UsrId)
{
if (UsrId !=0)
{
_userService.NewFunctionality(UsrId);
}
}
I expect that the function will start the job, CreateUserFunctionality and returns the view for me which is written in ContinueWith function. However in my case that continuewith part always waits for the CreateUserFunctionality to complete. My aim was to return the view immediatly and not wait for CreateUserFunctionality to finish, as It should continue in background. Please let me find out what I am missing here.
Ofcourse it will wait because you are using ContinueWith method.If you don't want to wait just start your task, and leave it.Return your ActionResult immediately:
var task = Task.Factory.StartNew(startMethod);
return RedirectToAction("Profile", "UserProfile");
From documentation:
Task.ContinueWith Method : Creates a continuation that executes
asynchronously when the target Task completes.
So it is working as expected.
I have a call I need to wait to finish to proceed. This is a begincreate for an opportunitySet and I'm having issues converting this into a task to wait for.
_context.BeginSaveChanges(OnCreateOpportunityComplete, _context.OpportunitySet);
/// <summary>
/// Callback method invoked when Account is done being created.
/// </summary>
/// <param name="result"></param>
private void OnCreateOpportunityComplete(IAsyncResult result)
{
try
{
_context.EndSaveChanges(result);
Opportunity createdOpportunity = result.AsyncState as Opportunity;
BeginRetrieveOpportunity(createdOpportunity.OpportunityId);
}
catch (SystemException se)
{
_syncContext.Send(new SendOrPostCallback(showErrorDetails), se);
}
}
private void BeginRetrieveOpportunity(Guid _id)
{
try
{
DataServiceQuery<Opportunity> query = (DataServiceQuery<Opportunity>)_context
.OpportunitySet.Where<Opportunity>(o => o.OpportunityId == _id);
query.BeginExecute(OnRetrieveOpportunityComplete, query);
}
catch (DataServiceQueryException dsqe)
{
_syncContext.Send(new SendOrPostCallback(showErrorDetails), dsqe);
}
}
Does anyone know how to handle this? I've tried numerous times and there seems to be some issues when creating the task. Or if there is any other way for this to wait for the multiple opportunities in the set to be created that would be awesome too.
EDIT: To clarify, the first line in the code is a line in my main function where after I gather all the opportunities I want to add I call the create methods to call the inserts provided in the CRM kit. This does insert, but by the time the program reaches its end more often than not all of the opportunities have not been created.
When making your method, make sure to mark it as "async". Then when calling it don't use the keyword "await" and you will get the its Task. From their you can call yourTask.Wait(). Which will wait until the execution of that method has finalized.
I couldn't really tell what method that you wanted to make async because your question is a little unclear, but if you follow this simple method, you should be able to make your own async method. Let's say you start with this method:
private int DoSomething(int someValue)
{
...
}
We can run it with async in this way:
private async int DoSomethingAsync(int someValue)
{
int result = await Task.Run(() => DoSomething(someValue));
// Do something with result here when it arrives
}
UPDATE >>>
You can use the Task.Wait method in Silverlight. If you take a look at the How to: Wait on One or More Tasks to Complete page on MSDN, you can get an idea of how it is done. This page is only for .NET 4 and 4.5 so I don't know if it will all work with Silverlight but you can try. You can still use your method in a Task like I showed you in the example above.