Modifying an item from a list shared by multiple threads

6

I have the following pseudo-code:

public void Associar(List<Data> dados)
{
   List<Task> tasks = new List<Task>();
   foreach(dado in dados)
   {
       tasks.Add(AdicionarAsync(dado));
   }
   Task.WaitAll(tasks.ToArray());

   Debug.WriteLine(dados.Select(e => e.Colecao).Sum(e => e.Count));
}

public async Task AdicionarAsync(Data dado)
{
   dado.Colecao = await consultanobanco(dado.Id);
}

The output of this code should always be 411 (equivalent to the sum of the records in the database). However, the result varies each time the Join method is run. I put Thread.Sleep(10); just to check if it was a competition problem and the problem was solved. What is the correct way to use a thread safe list to modify each item in a collection distributed across multiple Tasks?

Debugging the code a bit more, I noticed that the difference of values is actually in the line dado.Colecao = await consultanobanco();

Within method consultanobanco(); return is correct. However, when it reaches the assignment to dado.Colecao it goes wrong. Modifying from await consultanobanco(); to consultanobanco().Result the result is returned as expected.

Any reason for this behavior? What is the difference between await and .Result in this scenario?

    
asked by anonymous 22.06.2016 / 22:04

2 answers

1

Well, the problem occurs because you are changing the state of the object in a different thread than the one that reads it. More precisely, this line is giving problems, as you have seen:

dado.Colecao = await consultanobanco();

I'm not sure why .Result works, the best thing is not to spin too much into it. The .Net framework is free to decide whether or not to use a thread pool to run a task, perhaps in that case do not use it.

I'm not going to tell you what the solution to this problem is because I believe you have a bigger problem before this one. Note that the Collection property of all objects in the data list will have the same data, unless the behavior of consultanobanco is not deterministic. In fact it might even happen to have a race condition, where database data changes and only some of the objects on the list have that change.

It is best to read the data once and share it with all objects. For efficiency reasons too.

public asyn Task Associar(List<Data> dados)
{
   var dadosDoBanco = await consultanobanco();
   foreach(dado in dados)
   {
       dado.Colecao = dadosDoBanco;
   }

   Debug.WriteLine(dados.Select(e => e.Colecao).Sum(e => e.Count));
}
    
09.01.2017 / 00:46
0

According to this doubts your problem may be related to foreach foreach:

Try to use this code to see if the problem is fixed:


public void Associar(List dados)
{
   List tasks = new List();
   foreach(dado in dados)
   {
       var tempDado = dado;
       tasks.Add(AdicionarAsync(tempDado));
   }
   Task.WaitAll(tasks.ToArray());

   Debug.WriteLine(dados.Select(e => e.Colecao).Sum(e => e.Count));
}

public async Task AdicionarAsync(Data dado)
{
   dado.Colecao = await consultanobanco(dado.Id);
}

    
11.01.2017 / 12:23