Dear all,
I’m trying to run a computationally extensive operation on a background thread from within a custom component.
Currently, I’m using the C# async/await syntax.
Inside the SolveInstance() I’m starting a task on another thread like so:
protected override async void SolveInstance(IGH_DataAccess DA)
{
// Only start new run if we're not running already...
if (!Working)
{
// Set busy flag.
Working = true;
// Call the background worker.
// The worker will start working on it's own thread.
// The await keyword waits for worker to complete and *returns controll to
// the caller of SolveInstance*. That's why the GUI is still responsive.
List<object> result = await doWork(options["Duration"].Value);
// Once the worker has returned, we continue here.
// The returned list contains the actual return value and a success bool
// which indicates if the worker ran successfully (true) or was cancelled (false).
if ((bool)result[result.Count-1])
{
MessageBox.Show("Done! Result: " + (int)result[0]);
// Set outputs.
DA.SetData("Number1", (int)result[0]);
}
else if (!(bool)result[result.Count - 1])
{
MessageBox.Show("Cancelled.");
}
Working = false;
}
// If this is running already, consider killing the worker and restarting it.
else
{
;//TODO
}
}
The way this works is (as I hope to have understood): the doWork method gets called (it’s an async method as well and starts the background thread) and then – before the rest of SolveInstance is run – control is handed back to whoever called SolveInstance. This way, the GUI stays responsive which is what I need to show a progress bar on my component as well as handle mouse events on the component’s Cancel button. Once the doWork method has finished, control jumps to the rest of SolveInstance.
(Sorry for the missing mouse cursor…)
The problem, however, is that the outputs do not get set properly. While doWork is still running, they’re not set (which is OK for starters) but once the work is done, they are set to an additional branch and also not propagated to the next component. I think the problem is that ComputeData() – which is what calls SolveInstance() if I’m not mistaken – sets the component’s Phase property to Computed when SolveInstance() returns just after starting doWork(). Then, once doWork() is completed, the rest of SolveInstance() is run but the component itself has finished its usual stuff long ago.
To get this to work, I would need a better understanding of the single steps that are carried out during the solution of a component.
For example, I noticed that ComputeData() is called multiple times upon changing an input. The number of ComputeData() calls seems to somehow depend on the number of inputs but I could not get a clear understanding of it.
I have the feeling that it would be better to start doWork() from ComputeData() and only run SolveInstance() once that is done to set the outputs. If anyone could therefore shed some light into the ComputeData() method I’d be really grateful!
Sorry for the lengthly post btw…
Kind regards,
Paul