Search code examples
c#multithreadinglambdailnumerics

ILArrays as input arguments to BeginInvoke delegate


In my application, I have a background thread that performs calculations and pushes the resultant ILnumerics arrays to the view. I am having issues with ILNumerics arrays getting disposed off when I trigger the view update function with Control.BeginInvoke.

Are there any specific function rules to follow when passsing ILArrays as input arguments to BeginInvoke delegate?

Here is my sample code.

void IMainView.UpdateSpectrumData(ILInArray<float> wfmData)
        {
            if (InvokeRequired)
                {
                    BeginInvoke(new MethodInvoker(() => AddWfmToView(wfmData)), new object[] { wfmData });
                }
                else
                {
                    AddWfmToView(wfmData);
                }
            }
        }

        void AddWfmToView(ILInArray<float> wfmData)
        {
           using(ILScope.Enter(wfmData))
              {
                 // update panel
              }
         }

Solution

  • The problem is that the compiler will create an anonymous class for you behind the scenes. It is needed to capture the variables used in the lambda expression. And for that class the compiler will not follow the ILNumerics function rules. This is why you see premature disposals.

    The answer to your question is: ILArray is not supported in lambda expressions. Use it only with care and if you are aware of all subtleties related to it.

    In your case, your can work around the problem by falling back to ILNumerics.ILArray class usage. Declare an attribute in your container class (form/control?) which holds the data to be used for updating. From your updating routine you can access the attribute normally. For most common scenarios you will not need any synchronization. (But as always: think about it and make a conscious decision!)

    // a local attribute will 'transport' the data
    ILArray<float> m_data = ILMath.localMember<float>();
    
    public void UpdateView(ILInArray<float> wfmData) {
        using (ILScope.Enter(wfmData)) {
            m_data.a = wfmData;
            AddWfmToView(); 
        }
    }
    // the actual update method will not expose ILArray parameters. Hence we can use it in a lambda expression.
    void AddWfmToView() {
        if (InvokeRequired) {
            Invoke(new MethodInvoker(() => AddWfmToView()));
        } else {
            // access control here if necessary
            panel.Scene.First<ILLinePlot>().Update(m_data);
            panel.Configure();  
            panel.Refresh(); 
        }
    
    }