1

Since MS had said that both APM and EAP are outdated, TAP is the recommended approach to asynchronous programming in the .NET Framework. Then I want to convert my code from APM to TAP:

public class RpcHelper
{
    public void DoReadViaApm(IRpc rpc, BlockingCollection<ArraySegment<byte>> bc)
    {
        byte[] buf = new byte[4096];
        rpc.BeginRead(buf, 0, buf.Length,
            ar =>
            {
                IRpc state = (IRpc) ar.AsyncState;
                try
                {
                    int nb = state.EndRead(ar);
                    if (nb > 0)
                    {
                        bc.Add(new ArraySegment<byte>(buf, 0, nb));
                    }
                }
                catch (Exception ignored)
                {
                }
                finally
                {
                    DoReadViaApm(state, bc);
                }
            }, 
            rpc);
    }

    public void DoReadViaTap(IRpc rpc, BlockingCollection<ArraySegment<byte>> bc)
    {
        Task.Factory.StartNew(() =>
        {
            while (true)
            {
                Task<byte[]> task = rpc.ReadAsync();
                try
                {
                    task.Wait(-1);
                    if (task.Result != null && task.Result.Length > 0)
                    {
                        bc.Add(new ArraySegment<byte>(task.Result));
                    }
                }
                catch (Exception ignored)
                {
                }
            }
        }, TaskCreationOptions.LongRunning);
    }
}

public interface IRpc
{
    IAsyncResult BeginRead(byte[] buffer, int offset, int size, AsyncCallback callback, Object state);
    int EndRead(IAsyncResult asyncResult);

    Task<byte[]> ReadAsync();
}

The TAP method DoReadViaTap() use TaskCreationOptions.LongRunning, it looks very ugly. Can I make DoReadViaTap() looks better like DoReadViaApm() ?

svick
  • 236,525
  • 50
  • 385
  • 514

2 Answers2

0

I am not entirely sure what your code is supposed to do, but I guess it's some worker waiting for input? You could simplify it by using await and make the started operation asynchronous. Be aware that a task has a timeout, so you might want to set that appropriately or catch the exception.

public void DoReadViaTap(IRpc rpc, BlockingCollection<ArraySegment<byte>> bc)
{
    Task.Factory.StartNew(async () =>
    {
        while (true)
        {
            byte[] result = await rpc.ReadAsync();
            if (result.Length > 0)
            {
                bc.Add(new ArraySegment<byte>(result));
            }
            catch (AggregateException ignored)
            {
            }
        }
    }, TaskCreationOptions.LongRunning);
}
Benji Wa
  • 147
  • 7
  • You code looks better, but not what I expected. I want to kill the long running task, because DoReadViaApm() only use IO thread shortly. – Cauchy Song Mar 08 '16 at 03:23
  • You mean the task created by `Task.Factory.StartNew()`? You could use a cancellation token to check if that task is supposed to be cancelled. [Example](http://stackoverflow.com/a/19932396/2300387) – Benji Wa Mar 08 '16 at 14:45
  • Where did the `try` go? The `catch` won't compile without it. – svick Mar 09 '16 at 00:02
0

What you should do is to use async-await. And when you do that, you don't need to start a Task:

public async Task DoReadViaTap(IRpc rpc, BlockingCollection<ArraySegment<byte>> bc)
{
    while (true)
    {
        try
        {
            byte[] result = await rpc.ReadAsync().ConfigureAwait(false);
            if (result != null && result.Length > 0) // or result?.Length > 0 on C# 6
            {
                bc.Add(new ArraySegment<byte>(result));
            }
        }
        catch (Exception ignored)
        {
        }
    }
}

To me, this reads much better than the APM version.

Note that I don't think completely ignoring exceptions and infinite loops with no way to end them are good practices, I just copied those from your code.

svick
  • 236,525
  • 50
  • 385
  • 514
  • I'm looks for a infinite chained task construction: start: Task task = rpc.ReadAsync(); task.ContinueWith { bc.Add(new ArraySegment(result)); goto start: } – Cauchy Song Mar 09 '16 at 10:11
  • @CauchySong Why? It's going to be more messy than using `async`-`await`, just like like your APM code. – svick Mar 09 '16 at 11:19