locked
Chaining tasks RRS feed

  • Question

  • I'd like to chain asyncronous tasks, e.g. implement something like this:

     

    Code Snippet

    public class MyClass

    {

    public void Chain()

    {

    var newTask = Task.Create(ChainedAction, TaskCreationOptions.Detached | TaskCreationOptions.ManualStartup);

    var previousTask = Interlocked.Exchange(ref LastTask, newTask);

    if (previousTask != null)

    previousTask.ContinueWith(newTask, TaskContinuationKind.OnAny);

    else

    newTask.Start();

    }

     

    private Task LastTask;

    }

     

     

     

    Unfortunately I did not find such functionality it in June CTP.  Any suggestion on how can I implement task chaining? Will such functionality be available in future releases?

    Thursday, July 24, 2008 5:54 PM

Answers

  • Chernichkin, I believe functionality we have planned for an upcoming release will help you to do what you're trying to do in an easier way.  However, the two things I see you relying on here in your example are the abilities to create an unstarted task and later start it, and the ability to create a task and provide that task to ContinueWith, rather than providing a delegate to ContinueWith.  The latter is doable if the former is possible, even without API support, since you can always provide a delegate that starts the continuation task.  Additionally, the former is actually possible today, but with a hacky workaround (I'm not suggesting this is the right way to do it, but for now if you need this functionality, it would get you unblocked).  You could implement your needs on the June 2008 CTP with code something like:

     

    Code Snippet

    public class MyClass
    {
        public void Chain(Action chainedAction)
        {
            var starter = Future<bool>.Create();
            var newTask = starter.ContinueWith(delegate { chainedAction(); });
           
            var previousTask = Interlocked.Exchange(ref LastTask, newTask);

            if (previousTask != null)

                previousTask.ContinueWith(delegate { starter.Value = true; });
            else

                starter.Value = true;
        }

     

        private Task LastTask;
    }

     

     

    I'm using an unbound future as a trigger to start a Task (rather than a Start method, which doesn't currently exist).  The continuation then simply sets this unbound Future's value in order to cause it to immediately complete, which will cause its continuations to be scheduled, thus simulating a Start method.

     

    Monday, July 28, 2008 3:09 PM
    Moderator

All replies

  • Ok, seems I was a little bit stupid. I can use Monitor or SpinLock to perform safe task chaining

    Thursday, July 24, 2008 8:43 PM
  • Hi,

     

    It is better not to use locks if you don't need them because these are wait operations. You use a lock to work serially with a resource. In your case you want to chain two operations. If these operations are always sequential then why not use a single thread? Design methodologies? It is much better to use Invocations and if you need to keep working with a resource serially then use a thread as its single owner and talk to it (as a driver).

     

    Regards,

    Asaf

    Friday, July 25, 2008 8:00 AM
  • >>If these operations are always sequential then why not use a single thread?

    Because Chain() method could be invoked from different threads, but actual logic can be executed only afret previous request has been completed (consider Chain() is request queue, multiply producers posts requests but requests should be processed sequentially)

     

    >>use a thread as its single owner and talk to it

    Threads is quite expensive, I don't want to allocate thread for each resource.

     

    Actually I have similar logic implemented using .net IAsyncResult pattern, but i'ts quickly become complicated (I have to deal with time-outs, cancelation, exception hadling), so I'm looking for simplified patterns. Most of my components implements asynchronous methods not because it really should be asynchronous, but beause those methods involves network communications and I want to prevent thread blocking.

    Friday, July 25, 2008 12:47 PM
  • Hi,

     

    So, I'm not sure how many resources you plan to have on your system but I don't think that having threads should be too heavy for the system. Creating and destroying threads is very expensive but just having them is not. In the world of parallel computing you should handle exceptions, cancellations, timeouts, priorities, and two types of failure - request and operation. The problem is that no language today provides this functionality and writing parallel code today is similar to using assembly or C for object oriented code (constructor, virtual functions, inheritance) see the C implementation for COM interfaces. This is where we are today. SOA and UMDF are the closest infrastructures that you can use today.

     

    Regards,

    Asaf

    Friday, July 25, 2008 8:31 PM
  • Chernichkin, I believe functionality we have planned for an upcoming release will help you to do what you're trying to do in an easier way.  However, the two things I see you relying on here in your example are the abilities to create an unstarted task and later start it, and the ability to create a task and provide that task to ContinueWith, rather than providing a delegate to ContinueWith.  The latter is doable if the former is possible, even without API support, since you can always provide a delegate that starts the continuation task.  Additionally, the former is actually possible today, but with a hacky workaround (I'm not suggesting this is the right way to do it, but for now if you need this functionality, it would get you unblocked).  You could implement your needs on the June 2008 CTP with code something like:

     

    Code Snippet

    public class MyClass
    {
        public void Chain(Action chainedAction)
        {
            var starter = Future<bool>.Create();
            var newTask = starter.ContinueWith(delegate { chainedAction(); });
           
            var previousTask = Interlocked.Exchange(ref LastTask, newTask);

            if (previousTask != null)

                previousTask.ContinueWith(delegate { starter.Value = true; });
            else

                starter.Value = true;
        }

     

        private Task LastTask;
    }

     

     

    I'm using an unbound future as a trigger to start a Task (rather than a Start method, which doesn't currently exist).  The continuation then simply sets this unbound Future's value in order to cause it to immediately complete, which will cause its continuations to be scheduled, thus simulating a Start method.

     

    Monday, July 28, 2008 3:09 PM
    Moderator
  •  

    Thanks Toub.

    I've posted more detailed description of my problem in reply to you blog post "Useful Abstractions Enabled with ContinueWith". Treat this as features I'd like to see in the final release. Probably those features will be usefull not just for me.

    Monday, July 28, 2008 5:21 PM