简体   繁体   中英

When wrapping traditional asynchronous handlers to TPL Task<T> what happens to the Callback and State?

This MSDN page has the following example : The intent is to wrap an APM style task that can't be represented in the various Func<T1, T2, T3> overloads.

static Task<String> ReturnTaskFromAsyncResult()
{
    IAsyncResult ar = DoSomethingAsynchronously();     // <-- If this is an APM method, it's a bad example since most APM methods have a parameter for callback and state.
    Task<String> t = Task<string>.Factory.FromAsync(ar, _ =>
        {
            return (string)ar.AsyncState;
        });

    return t;
}

My question has to do with the function DoSomethingAsynchronously(); . Most APM functions I've seen require the parameter callback and state, which is missing in this sample.

QUESTION: What happens to the callback and state parameters in "DoSomethingAsynchronously"

What do I need to do to properly call a function similar to this? In my case I'm trying to wrap Azure Table calls like this

    Task CreateAsync(CloudTable tbl, CancellationToken token, object state)
    {
        ICancellableAsyncResult result = tbl.BeginCreate(null, state);  // Incorrect
        token.Register((o) => result.Cancel(), state);

        Task<bool> t = Task.Factory.FromAsync(result, _ =>
        {
            return (bool)result.AsyncState;
        });

        return t;
    }
    Task<bool> ExistsAsync(CloudTable tbl, CancellationToken token, object state)
    {
        ICancellableAsyncResult result = tbl.BeginExists(null, state);  // Incorrect
        token.Register((o) => result.Cancel(), state);

        Task<bool>  t = Task.Factory.FromAsync(result, _ =>
        {
            return (bool)result.AsyncState;
        });

        return t;
    }

I think you misunderstand what the state parameter is for. It's there for you : when you pass an object in there, you can then retrieve it by accessing AsyncState . (And similarly state in CancellationToken.Register() .) In this case, you don't need state for anything, so you should pass null in there. That also means there is no reason for the methods you're creating to have state parameter.

The callback parameter is for code to be executed when the asynchronous action completes. The overloads of FromAsync() that you need don't use this, so you should also pass null in there.

It also seems you're confused about what to put in the endMethod delegate. As its name suggests, you should call the EndXxx() method in there (in your case, that's EndCreate() and EndExists() ). If the whole operation returns something, it will be actually returned by the end method, so you should return that from the delegate. It will be then available as the result of the created Task . You can also perform some cleanup in the delegate. In your case, I think it makes sense to dispose of the cancellation registration there, since it won't be needed anymore.

So, your code should look something like:

Task CreateAsync(CloudTable tbl, CancellationToken token)
{
    ICancellableAsyncResult result = tbl.BeginCreate(null, null);
    var cancellationRegistration = token.Register(result.Cancel);

    return Task.Factory.FromAsync(result, ar =>
    {
        cancellationRegistration.Dispose();
        tbl.EndCreate(ar);
    });
}

Task<bool> ExistsAsync(CloudTable tbl, CancellationToken token)
{
    ICancellableAsyncResult result = tbl.BeginExists(null, null);
    var cancellationRegistration = token.Register(result.Cancel);

    return Task.Factory.FromAsync(result, ar =>
    {
        cancellationRegistration.Dispose();
        return tbl.EndExists(ar);
    });
}

For more information about this topic, have a look at Tasks and the APM Pattern from Stephen Toub.

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