On page 33 of Stephen Toub's book
http://www.microsoft.com/download/en/details.aspx?id=19222
There is the code
var pings = from addr in addrs.AsParallel().WithDegreeOfParallelism(16)
select new Ping().Send(addr);
foreach (var ping in pings)
Console.WriteLine("{0}: {1}", ping.Status, ping.Address);
and according to Stephen the better version
var pings = (from addr in addrs
select new Ping().SendTask(addr, null)).ToArray();
Task.WaitAll(pings);
foreach (Task<PingReply> ping in pings)
Console.WriteLine("{0}: {1}", ping.Result.Status, ping.Result.Address);
Stephen says the 2nd option is better because "Task abstraction can also be used to represent I/O-bound operations and without tying up a thread in the process."
But doesn't a Task just use the Threadpool(hence just using threads anyway) underneath anyway? So you are in fact tying up a thread?