Skip to content

Commit 88eb303

Browse files
committed
initial post
1 parent 751a85c commit 88eb303

File tree

1 file changed

+85
-0
lines changed

1 file changed

+85
-0
lines changed
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
---
2+
layout: post
3+
permalink: /show-and-tell/3/task-parallel-library.html
4+
date: 2012-04-18 23:00
5+
title: "Show and Tell #3 - Building Budgie with the Task Parallel Library (TPL)"
6+
author: "@shiftkey"
7+
comments: true
8+
---
9+
10+
## Introduction
11+
12+
13+
14+
## The problem with asynchronous code
15+
16+
When thinking about what [Budgie's](http://matthamilton.net/budgie) surface API was going to look like, I knew one thing: it would be asynchronous only. There would be no methods like `GetHomeTimeline()` that blocked and waited for Twitter to respond.
17+
18+
There are any number of ways to write asynchronous APIs. The one I've been most comfortable with to date (used in TweetSharp, another Twitter library that heavily informed Budgie) is to ask for a callback as a parameter to your method, and call that when your asynchronous task is done. For example:
19+
20+
public void DoStuff(string text, Action<DoStuffResult> callback)
21+
{
22+
// step 1: do stuff with text asynchronously
23+
DoStuffResult result = ...;
24+
25+
// step 2: call the callback with the result
26+
callback(DoStuffResult);
27+
}
28+
29+
This technique works really well, but with the advent of C# 5 and the new "async" and "await" keywords, a new approach became the obvious choice: using the Task Parallel Library.
30+
31+
The idea is simply to return a "hot" `Task` (that is, a task that is already running) from your method:
32+
33+
public Task<DoStuffResult> DoStuff(string text)
34+
{
35+
return Task.Factory.StartNew(t => ..., text);
36+
}
37+
38+
Note that the method above returns a `Task<T>`, which is a special kind of `Task` that returns a result. If your method doesn't need to return a value (like a void method) you would return a non-generic `Task` object instead.
39+
40+
The calling code can then decide what to do with that task. They might decide to block and wait for it to finish:
41+
42+
DoStuffResult result = DoStuff("hello world").Result;
43+
44+
Or they might use a continuation:
45+
46+
DoStuff("hello world").ContinueWith(result => { ... });
47+
48+
Or, in C# 5, they might use the non-blocking "await" keyword:
49+
50+
DoStuffResult result = await DoStuff("hello world");
51+
52+
Using the Task Parallel Library gives the caller some flexibility in that regard, and the fact that it integrates so seamlessly into the C# 5 syntax made it an obvious choice.
53+
54+
## Using the TPL with WebRequest
55+
56+
I decided to use good ol' `System.Net.WebRequest` when making my calls to Twitter, partly because it offered fine-grained control over the request and partly because I was doing "Google Driven Development" and a lot of code samples out there use it.
57+
58+
`WebRequest` offers an async API in the form of its `BeginRequest` and `EndRequest` methods, and as it turns out, the TPL gives us a really easy way to turn those calls into a `Task<WebResponse>`:
59+
60+
return Task.Factory.FromAsync<WebResponse>(request.BeginGetResponse, request.EndGetResponse, null);
61+
62+
That returns a running Task which is performing the request and will return the response in the `Result` property.
63+
64+
From there I can use a continuation (since Budgie is written using C# 4 and doesn't make use of the "await" keyword) to take action when the task completes:
65+
66+
return requestTask.ContinueWith(t =>
67+
{
68+
if (t.StatusCode != System.Net.HttpStatusCode.OK) return null;
69+
70+
Foo result = new Foo();
71+
72+
// populate result by deserializing the JSON returned in the response
73+
74+
return result;
75+
});
76+
77+
It's worth noting that `ContinueWith` in this case is returning a `Task<Foo>`, since I've passed it a `Func<Foo>` to execute. The fact that I'm "continuing" from a `Task<WebResponse>` doesn't mean I have to return that type.
78+
79+
All in all, it's pretty easy! Easier still had I written this code with C# 5, but I wanted to be able to build this from my home PC which doesn't have .NET 4.5 or the Async CTP installed.
80+
81+
## Wrapping Up
82+
83+
Using the TPL to create an asynchronous API is the logical choice in modern .NET development, and I wouldn't have done anything different in Budgie. I'm looking forward to the final release of VS11 and .NET 4.5, at which point I'll refactor the code to make use of the "await" keyword, but for now it's good to know that you can do this in a future-proof way with the current tools.
84+
85+
## About the Author

0 commit comments

Comments
 (0)