Task Capable Components, time-out, interrupt

I’ve implemented the TaskCapable abstract class in GH. from here: https://developer.rhino3d.com/guides/grasshopper/programming-task-capable-component/

and I notice it is passing a “CancelToken” while running the tasks. My question is how is this being used and how do you create your own CancelToken? I was thinking of creating a CancelToken for processes that take more than X amount of time to resolve or if you press a Key. Not sure if this is possible…

Thanks for your help.

You need to create a CancellationTokenSource instance. This source will emit tokens you can share with all methods which care about cancellation. If you then want to cancel a process, you do so on the source object.

Thanks David, this clears things up a bit. This is what I did in order to make all the instances of the TaskCapable component to use the same instance:

        protected override void SolveInstance(IGH_DataAccess DA)
        {

            if(RunCount == 1)
            {
                source = new CancellationTokenSource(1000);
            }

            if (InPreSolve)
            {
                var number = 0;
                DA.GetData(0, ref number);

                var task = Task.Run(() =>
                {
                    System.Threading.Thread.Sleep(10);
                    return number;
                }, source.Token);
                TaskList.Add(task);
            }

            if (!GetSolveResults(DA, out int data))
            {
                AddRuntimeMessage(GH_RuntimeMessageLevel.Error, "Couldn't do the request");
                return;
            }
            else
            {
                DA.SetData(0, data);
            }

        }

This correctly cancels the component. What seemed extremely odd though was that it was stopping while I was doing less that 100 iterations:

As you can see in the image I have 80 numbers per 10ms per calc ~850ms. However, shouldn’t those calcs be spread over all my threads (currently 8) so the total should be around 100ms? Maybe I am missing the point of TaskCapable?

The TPL does not guarantee that all tasks will be distributed over all processors. Windows is a multi tasking OS meaning no single application will ever be given access to all available resources.

There’s also always going to be overhead which means you’ll never really speed up by the processor count.

Finally there is code which runs outside of the threaded process, for example data conversions. Those who take the same amount of time either way.

Or, it could be that the data being used in the various tasks has thread locks build in, meaning only one thread is allowed to operate on it at any one time. That would totally nullify any attempt at speeding up the code via multithreading.

Since you’ve posted a very simple test case which does not suffer from locks or much overhead, the problem probably lies somewhere in the cancellation logic. But I’m looking at this code on a tablet, I have no way to actually run it until the weekend.

Hello
I don’t know if this is the cause (because you are not getting error message) but try:

if (source.IsCancellationRequested || !GetSolveResults (DA, out int data))

Sorry @psarras_st , is not that.
try this:

if (InPreSolve)
{
     // You must place "RunCount == 1" here,
     // because RunCount is reset when "InPreSolve" becomes "false"
     if (RunCount == 1)
          source = new CancellationTokenSource (100);

     var number = 0;
     DA.GetData (0, ref number);

     var task = Task.Run(() =>
     {
          Thread.Sleep(10);
          return number;
     }, source.Token);
     TaskList.Add (task);
     return; // You must here
}
               
if (source.IsCancellationRequested || !GetSolveResults (DA, out int data))
{
     AddRuntimeMessage (GH_RuntimeMessageLevel.Error, "Couldn't do the request");
     DA.AbortComponentSolution (); // You must abort the `SolveInstance` iteration
     return;
}
else
{
     DA.SetData (0, data);
}

jmv

1 Like

Thanks both! I tried different things with removing the cancelation all together and changing to an actual hard task instead of simulating with Thread.Sleep() and couldn’t find the issue.

I think @kitjmv hit the nail to the head though! I didn’t know I had to add the return on the first pass!

Thanks perfect! I amended a little bit the check to make it like so

            if (source.IsCancellationRequested)
            {
                AddRuntimeMessage(GH_RuntimeMessageLevel.Warning, "Run out of time!");
            }
            else if(!GetSolveResults(DA, out string data))
            {
                AddRuntimeMessage(GH_RuntimeMessageLevel.Warning, "Not running multithread");
            }
            else
            {
                DA.SetData(0, data);
            }

There is one thing i just found out,
“Thread.Sleep” blocks the use of all cores. (On my system at least).
I change “Thread.Sleep” to “for (var i = 0; i <10000000; i ++);” and the processor goes from 4% to 100% …

ah that is weird. Yeah the last thing I tried was with some dummy computations instead of the Thread.Sleep and it worked!