views:

536

answers:

1

I've recently started working on a new application which will utilize task parallelism. I have just begun writing a tasking framework, but have recently seen a number of posts on SO regarding the new System.Threading.Tasks namespace which may be useful to me (and I would rather use an existing framework than roll my own).

However looking over MSDN I haven't seen how / if, I can implement the functionality which I'm looking for:

  • Dependency on other tasks completing.
  • Able to wait on an unknown number of tasks preforming the same action (maybe wrapped in the same task object which is invoked multiple times)
  • Set maximum concurrent instances of a task since they use a shared resource there is no point running more than one at once
  • Hint at priority, or scheduler places tasks with lower maximum concurrent instances at a higher priority (so as to keep said resource in use as much as possible)
  • Edit ability to vary the priority of tasks which are preforming the same action (pretty poor example but, PredictWeather (Tommorrow) will have a higher priority than PredictWeather (NextWeek))

Can someone point me towards an example / tell me how I can achieve this? Cheers.

C# Use Case: (typed in SO so please for give any syntax errors / typos)

*note Do() / DoAfter() shouldn't block the calling thread

class Application ()
{

    Task LeafTask = new Task (LeafWork) {PriorityHint = High, MaxConcurrent = 1};
    var Tree = new TaskTree (LeafTask); 

    Task TraverseTask = new Task (Tree.Traverse); 
    Task WorkTask = new Task (MoreWork); 
    Task RunTask = new Task (Run);

    Object SharedLeafWorkObject = new Object ();

    void Entry ()
    {
        RunTask.Do ();
        RunTask.Join (); // Use this thread for task processing until all invocations of RunTask are complete
    }

    void Run(){                           
        TraverseTask.Do ();

        // Wait for TraverseTask to make sure all leaf tasks are invoked before waiting on them 
        WorkTask.DoAfter (new [] {TraverseTask, LeafTask});

        if (running){
            RunTask.DoAfter (WorkTask);   // Keep at least one RunTask alive to prevent Join from 'unblocking'
        }
        else    
        {
            TraverseTask.Join();
            WorkTask.Join ();
        }
    } 

    void LeafWork (Object leaf){
        lock (SharedLeafWorkObject)  // Fake a shared resource
        {
            Thread.Sleep (200); // 'work'
        } 
    }

    void MoreWork ()
    {
        Thread.Sleep (2000); // this one takes a while
    }
}


class TaskTreeNode<TItem>
{
    Task LeafTask; // = Application::LeafTask
    TItem Item;

    void Traverse ()
    {
        if (isLeaf)
        { 
            // LeafTask set in C-Tor or elsewhere
            LeafTask.Do(this.Item);

            //Edit
            //LeafTask.Do(this.Item, this.Depth); // Deeper items get higher priority
            return;
        }
        foreach (var child in this.children)
        {
            child.Traverse (); 
        }
    }
}
+4  A: 

There are numerous examples here:

http://code.msdn.microsoft.com/ParExtSamples

There's a great white paper which covers a lot of the details you mention above here:

"Patterns for Parallel Programming: Understanding and Applying Parallel Patterns with the .NET Framework 4"

http://www.microsoft.com/downloads/details.aspx?FamilyID=86b3d32b-ad26-4bb8-a3ae-c1637026c3ee&amp;displaylang=en

Off the top of my head I think you can do all the things you list in your question.

  • Dependencies etc: Task.WaitAll(Task[] tasks)
  • Scheduler: The library supports numerous options for limiting number of threads in use and supports providing your own scheduler. I would avoid altering the priority of threads if at all possible. This is likely to have negative impact on the scheduler, unless you provide your own.
Ade Miller