none
Visual Basic Expression are evaluated in parallel

    Question

  • Hi all,

    when an activity has multiple members that are Visual Basic Expressions, it seems that these expressions are evaluated in parallel before the Execute(NativeActivityContext context) function of our NativeActivity derived class is called.

    For example:

    class MyActivity : NativeActivity
    {
       public InArgument<string> TaskTitle { get; set; }
       public InArgument<string> TaskBody { get; set; }

       public override void Execute(System.Activities.NativeActivityContext context)
       {
           ...
           String title = TaskTitle.Get(context);
           String body = TaskBody.Get(context);
           ...
       }
    }

    We would have expected that the expressions behind these InArguments are only evaluated when TaskTitle.Get(context); and TaskBody.Get(context); is called in our Execute method. Instead they seem to be evaluated in preparation of the execution of the activity by WWF4.

    Some of these expressions rely on (lazy-loading) classes that are not thread safe, which leads to multi-threading related exceptions in collections because the expressions are evaluated in parallel.

    Question 1: is there a way to configure that the Visual Basic Expressions are evaluated from a single thread?

    Question 2: why are the expressions evaluated before an activity is started? They should only be evaluated when the code in the Execute override needs them.

    Any help would be greatly appreciated.

    Friday, December 03, 2010 3:19 PM

Answers

  • Arguments are always evaluated before activity.Execute(), I'm not sure of the exact reason but a benefit could be that that tracking of argument values becomes possible (even if the activity doesn't use the arguments).

    If you really need the expression to be evaluated during activity execution, (e.g. during each iteration of a loop) instead of using an argument, you can have an Activity<string> property, and schedule the expression evaluation manually, inside of Execute (the actual execution will be after Execute returns, but you can resume execution in a callback).

    public Activity<string> TaskBody { get; set; }

    ...

    context.ScheduleActivity<string>(TaskBody, bodyCompleteCallback);

    Tim

    Tuesday, December 07, 2010 12:44 AM
    Moderator
  • Expression don't execute in parallel, this following code will show that.

    class

     

     

    Program
    {
     
    static void Main(string[] args)
      {
       
    WorkflowInvoker.Invoke(new ExamineExpressionThreadActivity
       
    {
          FirstArgument =
    new InArgument<int>((env)=>Thread.CurrentThread.ManagedThreadId),
          SecondArgument =
    new InArgument<int>((env)=>Thread.CurrentThread.ManagedThreadId),

        });
      }
    }

     

     

    class ExamineExpressionThreadActivity : CodeActivity
    {
     
    public InArgument<int> FirstArgument { get; set; }
     
    public InArgument<int> SecondArgument { get; set; }
     
    protected override void Execute(CodeActivityContext context)
      {
       
    Console.WriteLine(this.FirstArgument.Get(context));
       
    Console.WriteLine(this.SecondArgument.Get(context));
      }
    }

    If you want your expression evaluate at the time you want it to, you cannot expose it as an InArgument, because the workflow runtime, like most traditional programming language, employs the eager evaluation strategy. An example of custom evaluation is given below:

    class Program
    {
     
    static void Main(string[] args)
      {
       
    WorkflowInvoker.Invoke(new CustomEvaluationActivity
       
    {
          FirstExpression = new LambdaValue<int>((env)=>Thread.CurrentThread.ManagedThreadId)

        });
      }
    }

    class

     

     

    CustomEvaluationActivity : NativeActivity
    {
     
    public Activity<int> FirstExpression { get; set; }

     

     

      protected override void Execute(NativeActivityContext context)
      {
       
    Console.WriteLine(Thread.CurrentThread.ManagedThreadId);
        context.ScheduleActivity<int>(this.FirstExpression, OnExpressionEvaluationCompleted);
      }

     
    void OnExpressionEvaluationCompleted(NativeActivityContext context, ActivityInstance completedInstance, int result)
      {
        Console.WriteLine(result);
       
    Console.WriteLine(Thread.CurrentThread.ManagedThreadId);
      }
    }

    Notice the workflow runtime always dispatch all work to the same thread, this eliminate the possibility of race.

     

     

    Monday, December 06, 2010 5:22 AM
    Moderator

All replies

  • Expression don't execute in parallel, this following code will show that.

    class

     

     

    Program
    {
     
    static void Main(string[] args)
      {
       
    WorkflowInvoker.Invoke(new ExamineExpressionThreadActivity
       
    {
          FirstArgument =
    new InArgument<int>((env)=>Thread.CurrentThread.ManagedThreadId),
          SecondArgument =
    new InArgument<int>((env)=>Thread.CurrentThread.ManagedThreadId),

        });
      }
    }

     

     

    class ExamineExpressionThreadActivity : CodeActivity
    {
     
    public InArgument<int> FirstArgument { get; set; }
     
    public InArgument<int> SecondArgument { get; set; }
     
    protected override void Execute(CodeActivityContext context)
      {
       
    Console.WriteLine(this.FirstArgument.Get(context));
       
    Console.WriteLine(this.SecondArgument.Get(context));
      }
    }

    If you want your expression evaluate at the time you want it to, you cannot expose it as an InArgument, because the workflow runtime, like most traditional programming language, employs the eager evaluation strategy. An example of custom evaluation is given below:

    class Program
    {
     
    static void Main(string[] args)
      {
       
    WorkflowInvoker.Invoke(new CustomEvaluationActivity
       
    {
          FirstExpression = new LambdaValue<int>((env)=>Thread.CurrentThread.ManagedThreadId)

        });
      }
    }

    class

     

     

    CustomEvaluationActivity : NativeActivity
    {
     
    public Activity<int> FirstExpression { get; set; }

     

     

      protected override void Execute(NativeActivityContext context)
      {
       
    Console.WriteLine(Thread.CurrentThread.ManagedThreadId);
        context.ScheduleActivity<int>(this.FirstExpression, OnExpressionEvaluationCompleted);
      }

     
    void OnExpressionEvaluationCompleted(NativeActivityContext context, ActivityInstance completedInstance, int result)
      {
        Console.WriteLine(result);
       
    Console.WriteLine(Thread.CurrentThread.ManagedThreadId);
      }
    }

    Notice the workflow runtime always dispatch all work to the same thread, this eliminate the possibility of race.

     

     

    Monday, December 06, 2010 5:22 AM
    Moderator
  • Arguments are always evaluated before activity.Execute(), I'm not sure of the exact reason but a benefit could be that that tracking of argument values becomes possible (even if the activity doesn't use the arguments).

    If you really need the expression to be evaluated during activity execution, (e.g. during each iteration of a loop) instead of using an argument, you can have an Activity<string> property, and schedule the expression evaluation manually, inside of Execute (the actual execution will be after Execute returns, but you can resume execution in a callback).

    public Activity<string> TaskBody { get; set; }

    ...

    context.ScheduleActivity<string>(TaskBody, bodyCompleteCallback);

    Tim

    Tuesday, December 07, 2010 12:44 AM
    Moderator