The difference between Task.Factory.FromAsync and

2019-03-15 12:52发布

问题:

I have very similar code when using the standard BeginRead and EndRead methods from the TcpClient and using Task.Factory.FromAsync.

Here are some examples.. Error handling code not shown.

Task.Factory.FromAsync:

private void Read(State state)
{
    Task<int> read = Task<int>.Factory.FromAsync(state.Stream.BeginRead, state.Stream.EndRead, state.Bytes, state.BytesRead, state.Bytes.Length - state.BytesRead, state, TaskCreationOptions.AttachedToParent);

    read.ContinueWith(FinishRead);
}

private void FinishRead(Task<int> read)
{
    State state = (State)read.AsyncState;

    state.BytesRead += read.Result;
}

Standard use of callbacks with BeginRead and EndRead:

private void Read(State state)
{
    client.BeginRead(state.Bytes, state.BytesRead, state.Bytes.Length - state.Bytes.Read, FinishRead, state);
}

private void FinishRead(IAsyncResult async)
{
    State state = (State)async.AsyncState;

    state.BytesRead += state.Stream.EndRead(async);
}

Both of these work fine but I am curious of their differences. The lines of code for both are pretty much equivalent and they both seem to perform the exact same function and have the same efficiency. Which one is preferable? What would you rather see in production code?

回答1:

I would much rather see Task<T> based code:

  • It provides for composition more easily; for example, it's reasonably easy to write a method which takes a collection of Task<T> tasks and returns another task which represents the majority verdict of those tasks. Likewise you can wait until any one of a collection of tasks has completed, etc.
  • It provides more flexible scheduling of where the continuation is run.
  • It allows the task itself to be returned with type safety and a lot more information than the somewhat anaemic IAsyncResult type returned by BeginRead.
  • It's simpler to specify error handling and cancellation with tasks than using the Begin/End model.
  • Task<T> is getting better language support in C# 5 with async/await - if your codebase already uses Task<T> pervasively, it'll be much easier to take advantage of this

Basically in modern code running on .NET 4, Task<T> is the idiomatic way of representing an on-going task. It's a much richer environment to work in than earlier attempts, and I would embrace it if you have the chance. Obviously if you're using .NET 3.5 or earlier, life is a bit harder, but I'm assuming that as you're asking the question, Task<T> is an option...