简体   繁体   中英

How do I avoid a race condition when using Dispatcher.BeginInvoke()?

The codes below just record the number of prime numbers between a and b. The c# async await works for my codes but the older dispatcher way gives odd result.when i click the button,i got the following result:
70435 primes between 2000000 and 2999999
67883 primes between 3000000 and 3999999
66330 primes between 4000000 and 4999999
65367 primes between 5000000 and 5999999
which is wrong since i should be <5 and begin with 1000000 . Someone help to explain the race condition here?

private void _button_Click(object sender, RoutedEventArgs e)
{
    //Go();
    Task.Run(() => Go1());
}
void Go1()
{
    Dispatcher.BeginInvoke(new Action(() => _button.IsEnabled = false));
    for (int i = 1; i < 5; i++)
    {
        int result = GetPrimesCount(i * 1000000, 1000000);
        Dispatcher.BeginInvoke(new Action(() =>
        _results.Text += result + " primes between " + (i * 1000000) +
        " and " + ((i + 1) * 1000000 - 1) + Environment.NewLine));

    }
    Dispatcher.BeginInvoke(new Action(() => _button.IsEnabled = true));

}
int GetPrimesCount(int start, int count)
{
    return ParallelEnumerable.Range(start, count).Count(n =>
    Enumerable.Range(2, (int)Math.Sqrt(n) - 1).All(i => n % i > 0));
}

This is an old gotcha: lambdas close over variables , not values , so the i accessed by the multiple updates is in fact the same i .

On a side note, I would personally rewrite the code not to use Dispatcher at all. There's always a better solution than Dispatcher :

private async void _button_Click(object sender, RoutedEventArgs e)
{
  _button.IsEnabled = false;
  var progress = new Progress<Tupe<int, int>>(update =>
  {
    _results.Text += update.Item1 + " primes between " + (update.Item2 * 1000000) +
        " and " + ((update.Item2 + 1) * 1000000 - 1) + Environment.NewLine));
  });
  await Task.Run(() => Go1(progress));
  _button.IsEnabled = true;
}
void Go1(IProgress<Tuple<int, int>> progress)
{
  for (int i = 1; i < 5; i++)
  {
    int result = GetPrimesCount(i * 1000000, 1000000);
    if (progress != null)
      progress.Report(Tuple.Create(result, i));
  }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM