4

I have the following method :

private void GetHistoricalRawDataFromTextFiles(MessageHeader header, HistoricalRawDataFromTextFileSubscriptionDto dto)
{
    var computeInstance = GetComputeInstance(dto.SubscriberId, dto.ComputeInstanceId);
    var task = computeInstance.GetHistoricalRawDataFromTextFiles(dto, progress => SendProgress(header.Sender, progress));

    task.ContinueWith(myTask =>
    {
        dto.TimeSeries = myTask.Result;
        Messenger.SendTo(SubscriberId, header.Sender, MessageType.Reply, MessageTopic.HistoricalRawDataFromTextFiles, dto);
    });
}

Method computeInstance.GetHistoricalRawDataFromTextFiles returns a Task<List<string>> and my question is

  • whether this is the correct way to pass header and dto into the lambda expression and task continuations. It is important that the header and dto instance values are captured within the lambda expression and task continuation at the time the outer method is called. The same method may be called again before the task from the previous call completes.
Yuval Itzchakov
  • 146,575
  • 32
  • 257
  • 321
Matt
  • 7,004
  • 11
  • 71
  • 117

2 Answers2

2

It is important that the header and dto instance values are captured within the lambda expression and task continuation at the time the outer method is called.

When using lambda expressions, what gets closed over is the variable, and not the value of that variable.

As long as header and dto aren't global variables which you modify each time before making the method call, you should be fine. If they are global variables, then you'll need to find a way to create a local copy for each of them. If they're reference types, you'll need to clone them, and if they're value types, you'll need to copy them to a local variable inside the lambda.

Yuval Itzchakov
  • 146,575
  • 32
  • 257
  • 321
  • they are not global variables in that they are globally accessible but, yes, `header` and `dto` change on each outer message call. So, the only way would be to clone them and then pass the clone into the lambda and task? – Matt Oct 04 '15 at 10:32
  • No, if they are not globally modifiable and you pass a new instance each time, you are fine with the code as is. – Yuval Itzchakov Oct 04 '15 at 10:34
  • Thanks, that makes it clearer, so, it just comes down to the fact that I pass in a new references to values of `header` and `dto`, correct? – Matt Oct 04 '15 at 10:53
  • Yes. Don't be worried about the captured variables from previous methods, you should be fine as is. – Yuval Itzchakov Oct 04 '15 at 11:14
  • Just to be careful : Captured variables [can](https://dotnetfiddle.net/uPdsKX) be modified on different threads. – Royi Namir Oct 04 '15 at 15:09
  • @RoyiNamir Of course they can be modified, but I don't think that's what he's worried about. – Yuval Itzchakov Oct 04 '15 at 16:05
  • @YuvalItzchakov, correct, with each outer method call I ensure that **always** different instances of `MessageHeader ` and `HistoricalRawDataFromTextFileSubscriptionDto ` will be passed in. I was worried about whether repeat method calls with different instances of the above will impact previous tasks that have already spun up but have not yet completed and hence the continuation has not yet been invoked. – Matt Oct 05 '15 at 04:31
1

I think that your question boils down to : "Is my method thread safe?"

I don't think it's related to your captured variable here.

Your method doesn't seem to access a shared/global source ( static/global variables or fields).(otherwise you'd need synchronization)

Even if this method was called by a multiple threads simultaneously then it still be thread safe and each call to GetHistoricalRawDataFromTextFiles would deal with a separate "realm" - This is because each thread has its own stack.

So whether or not you use captured variable (refers to the same memory location) - you still get unique dto and header for each iteration realm.

I don't see here any same shared memory location issue since each invocation (even if threaded) - gets its own space.

Again. this is assuming you're not using any global state.

Royi Namir
  • 144,742
  • 138
  • 468
  • 792
  • what do you define exactly as **global variable**? The outer method `private void GetHistoricalRawDataFromTextFiles(MessageHeader header, HistoricalRawDataFromTextFileSubscriptionDto dto)` can be invoked even on the same thread multiple times in short succession (each with different `header` and `dto` instances) and the idea is to spin up tasks and run their continuations that "capture" the `header` and `dto` that is passed into the method at the time. I guess I am worried that a second method call will impact the task and its continuation that was spun up during the first method call. – Matt Oct 04 '15 at 10:37
  • @MattWolf By global variable I mean a variable that is accessible from outside the function. [it doesn't have to be static though](https://dotnetfiddle.net/uPdsKX) – Royi Namir Oct 04 '15 at 15:07