5 Replies Latest reply on May 7, 2010 6:36 AM by MichaelvdV@Atos

    PI SDK Problem when using arcvalue method in a Thread

    ygalipeau

      i am working on a project in vb.net which i need to invoke the arcvalue in different thread (by using the threadpool object). when calling this function directly in the main thread, no problem, but when using different thread it crash.

       

       

       

      i have attached to this post a very simple project that use the arcvalue in 3 scenarios  to illustrate the crash:

       

      first one : call in main thread   OK

       

      second: call in thread pool    Error

       

      third call in stand alone thread    Error

       

      is there something that i am not doing right?

       

      thanks a lot

       

      yannick

        • Re: PI SDK Problem when using arcvalue method in a Thread
          ygalipeau

          ok i am replying myself,

           

          i am pretty sure that i found the reason of the problem. i would like others to confirm me that this is the case.

           

          i have 2 global objects which are:

          Private _piSDK As PISDK.PISDK = New PISDK.PISDK
          Private _piSrv As PISDK.Server

          and i am doing 3 different call to the arcvalue method with a point list. one from the main thread,

           

          one with a standalone thread :

          Private Sub btnStartFromThreadStandalone_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnStartFromThreadStandalone.Click
                  _serverName = Me.txtServerName.Text
                  Dim th As New Thread(AddressOf GetPIData)
                  th.Start()
          End Sub

          and one with the threadpool object:

           

          Private Sub btnStartFromThreadPool_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnStartFromThreadPool.Click
                  _serverName = Me.txtServerName.Text
                  ThreadPool.QueueUserWorkItem(New WaitCallback(AddressOf GetPIDataFromTreadPool))
          End Sub

          Private Sub GetPIDataFromTreadPool(ByVal state As Object)
                  GetPIData()
          End Sub

           

           

          what i found is that in my GetPIData, if i am creating the point list object like this:

          Dim PIPointList As New PISDK.PointList

          i have an error when adding points directly to the PIPointList with the add method with my global _piSrv objects:

          PIPointList.Add(_piSrv.PIPoints("BA:ACTIVE.1")) 

          but if i am doing

          Dim PIPointList As PISDK.PointList  'without the NEW

          and get the point list object with

          PIPointList = _piSrv.GetPoints("Tag = 'BA:ACTIVE.1'")

          it works.

           

          so is it that mixing pi objects from different thread cannot be done, even of those objects (Private _piSDK As PISDK.PISDK = New PISDK.PISDK
          Private _piSrv As PISDK.Server) are global?

           

          thanks,

           

          yannick

            • Re: PI SDK Problem when using arcvalue method in a Thread

              The problem you are seeing here does not have anything to do with whether you use the 'New' keyword or not - it is rather a threading problem. A cross-thread marshaling problem, more specifically.

               

              Because PI SDK in general is STA (single-threaded COM apartment) based, PI SDK methods can be invoked from one thread only. In a multi-threaded environment, you have to either marshal the objects you want to use in another thread, or have separate PI SDK hierarchies in each thread. Both approaches work. The first one is a little more work but consumes less memory.

               

              If you don't have separate PI SDK hierarchies per thread, COM has to serialize the method invocation and the results - this is called cross-thread marshaling. When doing that, you essentially end up working with a "proxy" to the original object rather than the actual object itself. An example of that is when you call the PIPoints collection from the Server object, from a different thread than where the Server object was created.

               

              With that said, you don't get a proxy by using global objects like you do: globals are just variables that are memory addresses, and when you build an object and stick it into a global, the same pointer you get from the creation gets dropped in the global. That means if another thread uses this global, it is just accessing that memory location and COM does not get a chance to substitute a proxy for the raw object pointer. So you are passing raw pointers to threads without marshaling, which will end in grief unless you are very careful. With the global, you are effectively bypassing COM; you would need to do something special to get the proxy.

               

              Not only can this cross-thread marshaling cause problems (as in your case), it may also have a performance impact. The easiest solution is to make sure you instantiate a top-level PI SDK object that's local to each thread you need PI SDK calls from.

               

              On the other hand, the GetPointsSQL method (could be the GetPoints method, for that matter) succeeds with that serialization because it's got some special code to handle the query through a background thread...

               


              You can read more on this in the "Threading" article of the PI SDK Programming Reference. If you need more information on COM, threads, marshaling, etc., you might want to consider the book "Essential COM" from Don Box.

               

              Hope this helps!