Tuesday, October 10, 2017

Passing data to a Task using parameter to avoid race condition

Requirements

Consider the scenario where you want to pass the current value of i to a new task that gets created in a for loop. We want each task to have a unique value of i (0...9).

The wrong approach (closures)

In general, it is a bad idea do use closures to pass data to a task when the value will change before the task starts. Below is a classic race condition such that the value of i is not read until the task starts, but before most of the tasks start all the tasks have been created. This will result in gotParamNow being equal to 10 (or any value really, but not what we intended).

for (int i=0; i<10; i++)
{
Task.Factory.StartNew( () =>
{
int gotParamNow = i;
}
);
}

The right approach (parameter)


If you want to pass data when the Task is CREATED (not when started as it would do when using closures) then you can pass the data to the StartNew method as shown below.

for (int i=0; i<10; i++)
{
Task.Factory.StartNew( (arg) =>
{
int gotParamNow = (int)arg;
},
i
);
}

In the code above the value i changes over time, so we want to pass it to the StartNew method so that when the task starts the data has the value that was passed to it and will result in each task having a unique value of i passed to it and thus gotParamNow will be unique as well.

No comments: