-
Introducing TPL
If your processing isn’t driven by a LINQ query, you can still use the technology that PLINQ draws on: TPL. Fundamentally, TPL lets you create loops where, instead of processing each pass through the loop after the previous pass completes, passes through the loop are executed in parallel. If you have a quad-core computer, for instance, a loop can potentially be completed in one-third of the time because three passes through the loop can run simultaneously on each core.
Without TPL you would write this code to process all the elements of the Orders collection:
For Each o As Order In le.Orders
o.RequiredDate.Value.AddDays(2)
Next
With TPL, you call the ForEach method of the Parallel class, passing the collection and a lambda expression to process the items in the collection:
System.Threading.Tasks.Parallel.ForEach(
le.Orders, Sub(o)
o.RequiredDate.Value.AddDays(2)
End Sub)
By using the Parallel ForEach, each instance of the method can be processed simultaneously on a separate processor. If each operation takes 1 millisecond, and enough processors exist, all of the Orders can be processed in 1 millisecond instead of 1 millisecond multiplied by the number of Orders.
Any complex processing placed in a lambda expression will quickly get very difficult to read, so you’ll often want to call some method from your lambda expression as this example does:
System.Threading.Tasks.Parallel.ForEach(
le.Orders, Sub(o)
ExtendOrders(o)
End Sub)
...
Sub ExtendOrders(ByVal o As Order)
o.RequiredDate.Value.AddDays(2)
End Sub
Under the hood, TPL takes the members of the collection and partitions them into a set of separate tasks that it distributes over
the cores on your computer, queuing them up for execution. As each task finishes, freeing up its code, the TPL scheduler pulls another task from the queue to keep the core busy. You can also use the For method to create a loop based on an indexed value.
However, the real power in TPL comes when you create a custom Task. A Task can be created and then launched using its Start method. However, it’s easier to use the Task class’ static Factory object, whose StartNew method will both create the Task and launch it. To use the StartNew method you only need to pass a lambda function. If your function returns a value, you can use the Generic version of the Task object to specify the return type.
This example creates and starts a Task for each Order Detail object that calculates the Order Detail extended price. The Tasks are added to a List as
they’re created and launched. The code then loops through the List retrieving the results. If I ask for a result before it’s
calculated, the second loop will pause until that Task completes:
Dim CalcTask As System.Threading.Tasks.Task(Of Decimal)
Dim CalcTasks As New List(Of System.
Threading.Tasks.Task(Of Decimal))
For Each ord As Order_Detail In
le.Order_Details
Dim od As Order_Detail = ord
CalcTask = System.Threading.
Tasks.Task(Of Decimal).Factory.StartNew(Function() CalcValue(od))
CalcTasks.Add(CalcTask)
Next
For Each ct As System.Threading.Tasks.Task(Of Decimal) In CalcTasks
totResult += ct.Result
Next
If I’m lucky, every Task will complete before I ask for its result. But even if I’m unlucky, I’ll still finish earlier than if I ran each
Task sequentially.
Where the output of a Task depends on another Task completing, you can create dependencies between Tasks or groups of Tasks. The easiest technique is to use a Wait method, which causes your application to stop executing until all of the Tasks in an array complete:
Dim tsks() As System.Threading.Tasks.Task = {
Task(Of Decimal).Factory.StartNew(Function() CalcValue(le.Order_Details(0))),
Task(Of Decimal).Factory.StartNew(Function() CalcValue(le.
Order_Details(1)))
}
System.Threading.Tasks.Task.WaitAll(tsks)
A more sophisticated approach is to use the ContinueWith method of a Task object to cause an instance of a Task to proceed (on the same thread) after some other task completes. This example launches multiple threads, each of which calculates the value of the Order Detail, but only after some other operation has been completed on the Order Detail:
For Each ordd As Order_Detail In le.Order_Details
Dim od As Order_Detail = ordd
Dim adjustedDiscount As New Task(Sub() AdjustDiscount(od))
Dim calcedValue As Task(Of Long) =
adjustedDiscount.ContinueWith(Of Long)(Function() CalcValue(od))
adjustedDiscount.Start
Next
Source of Information : Visual Studio Magazine August 2010
Subscribe to:
Post Comments (Atom)
0 comments: