简体   繁体   中英

How to Force Task To Wait for Child Tasks?

I have to use an API that mandates a class that implements its callback interface as a parameter to an authentication method.

public class CallBack : ICallBack
{
    public object Response;
    public void OnSuccess(object response)
    {
        Response = response;
    }
    public void OnException(Exception ex) { }
}

Authentication Call

public async Task<bool> LoginAsync(string username, string password)
{
    CallBack callback = new CallBack();
    await Task.Factory.StartNew(
        () => userService.Authenticate(username, password, callback),
            TaskCreationOptions.AttachedToParent);
    return callback.Response is User ? true : false;
}

The problem is that LoginAsync finishes before the callback is invoked. I hoped that by starting the Authenticate call using TaskCreationOptions.AttachedToParent , it would propagate down to any child tasks started in Authenticate but it does not.

You should use a TaskCompletionSource object to wrap your callback based async method into a awaitable task.

I assume your ICallBack is like this :

public interface ICallBack
{
    void OnSuccess(object response);
    void OnException(Exception ex);
}

So you can implement LoginAsync like this :

public async Task<bool> LoginAsync(string username, string password)
{
    var tcs = new TaskCompletionSource<object>();
    ICallBack callback = new CallBackAsync(tcs);
    userService.Authenticate(username, password, callback);
    var result = await tcs.Task;
    return result is User ? true : false;
}

public class CallBackAsync : ICallBack
{
    private TaskCompletionSource<object> _tcs;
    public CallBackAsync(TaskCompletionSource<object> tcs)
    {
        _tcs = tcs;
    }

    public void OnSuccess(object response)
    {
        _tcs.TrySetResult(response);
    }
    public void OnException(Exception ex) {
        _tcs.TrySetException(ex);
    }
}

For the quick explanation, when you use Task.Factory.StartNew(), the completion of the task is raised at the end of the lambda expression. But in your case this occurs before CallBack.OnSuccess call. So the result is not set.

The TaskCompletionSource class allow you to fully control when the task completion must occurs.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM