Asynchrony and C#

I am currently rewriting connection handling and related code for InWorldz, and I’m changing most of it to be async because many of the operations that have to complete end up waiting for network I/O. I’ve written a few of our other services in C++ following async programming patterns using Boost.Asio, and although following program flow and making sure all error and success cases are handled properly can be a challenge, the performance benefits of using an async flow when coupled with operating system supported async primitives can not be denied. Once you get used to the flow of it, async programming can be a real asset to have in your toolbox, and in some scenarios actually simplify your code.

I’ve always felt that the model of using what are essentially function callbacks for every async call was expensive in terms of the amount of code you end up having to write. This can get a bit brain numbing especially in C++ because you have to fill the functions into both the header files of a class as well as the .cpp implementation file. With the advent of C++11 and lambda functions, the extra code could definitely be minimized, but there is still some elegance missing that I just couldn’t put my finger on. Then I found .NET 4.0’s await and async keywords.

Let’s take a look at a simple (but contrived) example using boost ASIO, and then another example using C#, .NET 4, and await/async.

Badly written C++ example

void WorkerThingy::doPart1(boost::function resultCallback)
{
  boost::asio::async_write(client->getConn(),
    boost::asio::buffer(*data1, data1->size()),
    boost::bind(&WorkerThingy::doPart2, shared_from_this(),
      boost::asio::placeholders::error, resultCallback));
}

void WorkerThingy::doPart2(const boost::system::error_code& part1error, 
  boost::function resultCallback)
{
  boost::asio::async_write(client->getConn(),
    boost::asio::buffer(*data2, data2->size()),
    boost::bind(&WorkerThingy::doPart3, shared_from_this(),
      boost::asio::placeholders::error, resultCallback));
}

void WorkerThingy::doPart3(const boost::system::error_code& part2error,
  boost::function resultCallback)
{
  boost::asio::async_write(client->getConn(),
    boost::asio::buffer(*data3, data3->size()),
    boost::bind(&WorkerThingy::itsDone, shared_from_this(),
      boost::asio::placeholders::error, resultCallback));
}

void WorkerThingy::itsDone(const boost::system::error_code& part3error, 
  boost::function resultCallback)
{
	resultCallback(!part3error)
}

bool WorkerThingy::doIt()
{
	doPart1(boost::bind(&this->onDoItResult, this));
	
	boost::unique_lock lock(this->mut);
	while(!this->data_ready)
	{
		this->cond.wait(lock);
	}

	return this->success;
}

void WorkerThingy::onDoItResult(bool success)
{
	//...process the result we got
	boost::lock_guard lock(this->mut);
	this->data_ready=true;
	this->success = success;
	this->cond.notify_one();
}

In this really terrible example that probably doesn’t compile, doIt() is called from a synchronous code path, and has 3 async parts that it will call one by one to get its network tasks finished. DoIt() calls doPart1(). When the send of the data from doPart1() has completed doPart2() is called (with no error checking, but that’s another story). Then on the completion of doPart2(), doPart3() is called by ASIO. Once the data from doPart3() is sent, ASIO calls itsDone() which calls the passed in resultCallback, which in turn was bound to our onDoItResult() method. OnDoItResult() signals the condition and frees up the caller of doIt() and finally returns whether the send in doPart3() succeeded.

The wait primitives and long call chain gets confusing after a while, and sometimes when we’re doing real work there are many more calls chained in to complete a task.

Let’s see what this might look like in C# and .NET 4 using await and async.

Badly written, but more readable C# example

namespace AwaitSample
{
    class WorkerThingy
    {
        private NetworkStream m_stream;

        private byte[] data1;
        private byte[] data2;
        private byte[] data3;

        private async Task DoItInternal()
        {
            await m_stream.WriteAsync(data1, 0, data1.Length);
            await m_stream.WriteAsync(data2, 0, data2.Length);
            await m_stream.WriteAsync(data3, 0, data3.Length);

            return true;
        }

        public bool DoIt()
        {
            var task = DoItInternal();
            task.Wait();
            return task.Result;
        }

    }
}

When the synchronous caller executes DoIt(), the function calls DoItInternal(). This calls the first WriteAsync() method, and then returns to the caller immediately while it is running. The caller ( DoIt() ) is at this point waiting for the task to complete by calling task.Wait(). After the WriteAsync(data1) call completes, the task moves to the WriteAsync(data2) and DoItInternal() is suspended until that call finishes. Finally, WriteAsync(data3) is executed, and when it returns, we finally return from the task which unblocks task.Wait() and gets us the result.

As you can see this is super useful and really cleans up the call chain for async execution. We also don’t have to await each of the WriteAsync tasks if we would like to have them executed in parallel, and this is very easily done without adding a bunch of functions and trying to coordinate everything.

One thought on “Asynchrony and C#

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s