Passing arguments with changing values to Task — B

2019-02-08 00:56发布

问题:

Scenario: An asynchronous task in a loop executes a method containing arguments that change as the program continues:

while(this._variable < 100)
{
    this._variable++; 
    var aTask = Task.Factory.StartNew(() =>
    {
        aList.add(this._variable);
        update(this._savePoint);
    });
}

If the loop runs faster than the tasks complete, will the list add the current value of the variable or is the variable saved locally and the original value added?

回答1:

Closures close over variables, not values. Therefore, incrementing _variable can alter the behavior of the task that refers to it.

You can prevent this by making a local copy:

while (this._variable < 100)
{
    this._variable++;
    int local = _variable;
    var aTask = Task.Factory.StartNew(() =>
    {
        aList.add(local);
        update(this._savePoint);
    });
} 

Or you could pass the value to the task as state:

while (this._variable < 100)
{
    this._variable++;
    var aTask = Task.Factory.StartNew(object state =>
    {
        aList.add((int)state);
        update(this._savePoint);
    }, this._variable);
} 

These both work by copying the value of _variable to a new temporary variable. In the first case the local variable is defined inside the scope of the loop, so you get a new one for every iteration. In the second case, you make a copy of the value of _variable when you pass it to the Task as the state argument. If _variable were a reference type, these solutions wouldn't work; you'd have to perform a clone.