I am just working on a Grasshopper plugin which requires multi-threading. At the beginning I had some problem with implementing the asynchronized event and expiring the grasshopper solution from another thread. It seams that this topic is already discussed and there are some snipped codes you may find on Rhino forum , however, I could not find a complete implementation of asynchronous programming which uses the AsyncCompletedEventArgs within the Grasshopper component. Today I managed to use Asynchronized operation and I would like to share a few files as template which helps you to put up your code and take advantages of an asynchronous program. Please remember that asnych-programming is useful when you want to avoid the frozen UI or you are working with server side applications in which you need to wait for data to be downloaded or uploaded and also be noted that asynchronous programming does not always make things faster ! So if you are ready let’s jump into some code..

 

Grasshopper Component

Here is a C# implementation of the grasshopper component which contains an asynchronous program.  You have two flags defining the state of the program. Once you run the program you set the taskIsRunning to true and once all tasks are completed you set it back to false. The taskIsCompleted flag is set to true in the same event however it is reset when the solution of the component is updated. In SolveInstance() method when no task is running and no tasks is completed (meaning component is updated when no task is running) we create an instance of our asynchronous program and assign handler to its events. As you can see in the code below when all tasks are completed we expire the solution and updated the outputs.

CODE
    /// <summary>
    /// this is an example of implementation of Asynchronous Programming within grasshopper component
    /// </summary>
    public class AsychGHComponent : GHCustomComponent
    {
        /// <summary>
        /// it is true when task is running and 
        /// then it is set to false after completation of all tasks.
        /// </summary>
        bool taskIsRunning = false;
        /// <summary>
        /// it is true just after completaion of all tasks and 
        /// then it is set to false after updating the data 
        /// </summary>
        bool taskIsCompleted = false;
        /// <summary>
        /// if you wish to allow user to cancel operation
        /// </summary>
        CancellationTokenSource cts;
 
        /// <summary>
        /// instance of the asynched-programm
        /// </summary>
        AsynchronousProgram _myProgramm;
 
        #region constructor
        /// <summary>
        /// this is an example of implementation of 
        /// </summary>
        public AsychGHComponent()
          : base("AsychGHComponent", "AP",
"An example of Grasshopper plugin & Event based asynchronous programming in C#", "mainCat", "subCat") { } #endregion #region implementation of the base class /// <summary> /// Registers all the input parameters for this component. /// </summary> protected override void RegisterInputParams(GH_Component.GH_InputParamManager pManager) { //---------------------------------------------- //------------- your code ---------------------- //---------------------------------------------- } /// <summary> /// Registers all the output parameters for this component. /// </summary> protected override void RegisterOutputParams(GH_Component.GH_OutputParamManager pManager) { //---------------------------------------------- //------------- your code ---------------------- //---------------------------------------------- } /// <summary> /// This is the method that actually does the work. /// </summary> /// <param name="DA">The DA object can be used to retrieve data from input parameters and /// to store data in output parameters.</param> protected override void SolveInstance(IGH_DataAccess DA) { // check if the task is running then ignore the update if (taskIsRunning) return; if (taskIsCompleted) { //task is just completed we can update the solution ! //------------------------ //set the output data here //------------------------ // for the next call make sure we set the flag off again taskIsCompleted = false; // dispose the cancellationtoken cts.Dispose(); } else { // the taskIsCompleted flag is not true therefore we can start new task ! //-------------------------------- cancellation ------------------------- // you may allow user to cancel the operation by any means // simply call cts.Cancel(); cts = new CancellationTokenSource(); //---------------------------------------------------------------------- //-------------------------------- run asynched programm---------------- _myProgramm = new AsynchronousProgram(); _myProgramm.AllTasksAreComplete += _myProgramm_AllTasksAreComplete; _myProgramm.OneTaskComplete += _myProgramm_OneTaskComplete; // set the flag so we don't call RunAsynchProgram twice taskIsRunning = true; _myProgramm.RunAsynchProgram(cts.Token); //---------------------------------------------------------------------- } } private void _myProgramm_OneTaskComplete(object sender, AsyncTaskCompleteArgs e) { // one taks was completed. // you can report the progress / update preview / etc.... if (e.Error==null) { //report progress or update UI } else { // check for user cancellation or maange excpetions... } } private void _myProgramm_AllTasksAreComplete(object sender, EventArgs e) { // all tasks are completed we set the flag and expire the solution taskIsCompleted = true; taskIsRunning = false; // expire the solution, this jupms back to SolveInstance() ExpireSolution(true); } public override Guid ComponentGuid { get { return new Guid("e2d943bd-d860-4f71-b0bd-a366d2200451"); } } #endregion }
Asynchronous Program

In this class we use AsyncOperationManager to wrap our operation into an AsyncOperation and then assign a call back to it. The call back will fire an event when the operation is completed or encountered an error. Using  AsyncTaskCompleteArgs class we wrap our data which we have got from the operation into an event argument and then raise an event which is then catch from grasshopper plugin. Note that event is raised after completion of each task, therefore you must find a way to know when all tasks are completed , in this example I assume we know the total number of tasks and hence when we count the number of call backs and if it reaches the total number of task we raise our  AllTasksAreComplete event which expires the grasshopper solution. You can also report the progress or update the grasshopper UI while listening to the OneTaskComplete event from GH component

CODE
 /// <summary>
 /// this is an example of Async programming in DotNet
 /// </summary>
 public class AsynchronousProgram
 {
     /// <summary>
     /// call back to raise the <c>RowComplete</c> event
     /// </summary>
     private SendOrPostCallback taskCompletedCallBack;
     /// <summary>
     /// somehow record the number of tasks you need to run. if that's not possible
     /// then you may need to find a way to know if all tasks are completed.
     /// </summary>
     int numberOFTasks ;
     /// <summary>
     /// number of tasks which are completed.
     /// </summary>
     int completedTasks = 0;
 
     #region constructor
     /// <summary>
     /// construct the graph , note you must call <c> ComputeGraphAsynch()</c> to calculate the Data
     /// </summary>
     public AsynchronousProgram()
     {
         //-------------------------------------
         //---------------your code-------------
         //-------------------------------------
 
         // create an instance of the callback, this callback fires the event for task completion
         taskCompletedCallBack = new SendOrPostCallback(TaskCompleted);
     }
 
     #endregion
 
 
     #region Events and Delegates
     /// <summary>
     /// raises when computation of a taks is completed
     /// </summary>
     public event EventHandler<AsyncTaskCompleteArgs> OneTaskComplete;
     /// <summary>
     /// raises when computation of all tasks are completed
     /// </summary>
     public event EventHandler<EventArgs> AllTasksAreComplete;
     /// <summary>
     /// a delegate which holds necessar data to compute a task
     /// </summary>
     /// <param name="asyncOperation"></param>
     /// <param name="state"></param>
     /// <param name="cts"></param>
     private delegate void TaskCallBack(AsyncOperation asyncOperation, int state, CancellationToken cts);
     #endregion
 
     #region implementatio of asycnh operations
     /// <summary>
     /// the main method you need to call from UI thread (grasshopper plug-in)
     /// listen to the methods <c>OneTaskComplete</c> and <c>AllTasksAreComplete</c> for completion
     /// </summary>
     /// <param name="kuka"></param>
     /// <param name="singularityCheck"></param>
     /// <param name="cts"></param>
     public void RunAsynchProgram(CancellationToken cts)
     {
         //you may create multiple operation using a loop or recursion or ant other ways of iteration
         //just make sure each operation has a unique id (state) 
//here I am just usig the iterator for simplicity
for(int i=0;i< numberOFTasks;i++) { // create an asynch operation AsyncOperation asyncOperation = AsyncOperationManager.CreateOperation(i); // build a call back TaskCallBack myCallBack = new TaskCallBack(computeTaskAsynch); // enque the task using invoke. The call back will call the operation on the first possible //thread and await its completion. when the tast is completed the OnTaskComplete() method //gets called and an event is fired myCallBack.BeginInvoke(asyncOperation, i, cts, null, null); } } /// <summary> /// this method is used for call back only ! /// compute a single task (given state is the index of the row to be computed) /// </summary> /// <param name="asyncOperation"></param> /// <param name="state">the index of the row in the graph (used also as Id for the action)</param> /// <param name="token">The cancellation token from the UI thread</param> void computeTaskAsynch( AsyncOperation asyncOperation, int state, CancellationToken token) { AsyncTaskCompleteArgs e; try { // do your stuff var data = DoSomething(state,token); // build the event argument e = new AsyncTaskCompleteArgs(null, false, state, data); } catch (Exception exception) { // user may cancel the operation or an exception may be thrown e = new AsyncTaskCompleteArgs(exception, false, state, null); } // post the operation and wait for the completion event(s) asyncOperation.PostOperationCompleted(taskCompletedCallBack, e); } /// <summary> /// do the actual work /// </summary> /// <param name="taksId"></param> private object DoSomething(int taksId, CancellationToken token) { // check for uer interruption token.ThrowIfCancellationRequested(); //---------------------- //------your code------- //---------------------- return null; // you can return something and pass it via event argument } /// <summary> /// raise the RowComplete event, and if all rows are completed then raises the GraphComplete event /// </summary> /// <param name="e"></param> void OnTaskComplete(AsyncTaskCompleteArgs e) { // inrement the number of tasks which are completed . completedTasks++; // in case of the error see if the user has canclled if (e.Error != null) { //---------------------- //------your code------- //---------------------- } else { //---------------------- //------your code------- //---------------------- } // raise the event for a single task completion OneTaskComplete?.Invoke(this, e); // if all tasks are completed then raise the AllTasksAreComplete event if (completedTasks == numberOFTasks) AllTasksAreComplete?.Invoke(this, new EventArgs()); } /// <summary> /// calls the function <c>OnRowComplete()</c> from the call back /// </summary> /// <param name="state"></param> void TaskCompleted(object state) { OnTaskComplete((AsyncTaskCompleteArgs)state); } #endregion }
Async Event

Lastly you need to implement the AsyncCompletedEventArgs class whihc is used to transfer data by an async event. Simply pack your data and (instead of the object argument in template code) and you can read it from Grasshopper component. It is always uesfull to send data about the particular task which is completed as well as the number of completed tasks in case you want to display some progress.

 

CODE
/// <summary>
    /// implement <c>AsyncCompletedEventArgs</c> class 
    /// </summary>
    public class AsyncTaskCompleteArgs : AsyncCompletedEventArgs
    {
        //----------------------------------------------
        //------------- your internal data--------------
        //----------------------------------------------
        // data to be transgered by event argument
        public object Data { get; private set; }
        public AsyncTaskCompleteArgs(Exception exception, bool cancelled, int userState, object mydata) 
: base(exception, cancelled, userState) { Data = mydata; } }