Gyula Soós
Gyula Soós

Reputation: 487

How can I achieve that application remains responsive, while threads work in the background?

I have a simple app that will tell me the number of primes within a range:

    private void btnMultiThread_Click(object sender, RoutedEventArgs e)
    {
        var numbers = Enumerable.Range(2, range - 1);
        var totalPrimes = numbers
            .AsParallel()
            .Where(n => Enumerable.Range(2, (int)Math.Sqrt(n) - 1).All(i => n % i != 0))
            .Count();
        MessageBox.Show(totalPrimes.ToString());
    }

The problem is, that while this whole thing is counting, the wpf app remains unresponsive: for example the button I clicked remains pushed in, the application window cannot be moved or even closed.

What do I do, to fix this? Thanks

Upvotes: 1

Views: 57

Answers (2)

Black Frog
Black Frog

Reputation: 11713

Excerpt from Microsoft about Chaining Tasks by Using Continuation Tasks:

In asynchronous programming, it is very common for one asynchronous operation, on completion, to invoke a second operation and pass data to it. Traditionally, this has been done by using callback methods. In the Task Parallel Library, the same functionality is provided by continuation tasks. A continuation task (also known just as a continuation) is an asynchronous task that is invoked by another task, which is known as the antecedent, when the antecedent finishes.

Here is one way:

private void btnMultiThread_Click(object sender, RoutedEventArgs e)
{
    Task<int>.Factory.StartNew(() =>
    {
        var numbers = Enumerable.Range(2, range - 1);
    var totalPrimes = numbers
            .AsParallel()
            .Where(n => Enumerable.Range(2, (int)Math.Sqrt(n) - 1).All(i => n % i != 0))
            .Count();
        return totalPrimes;
    }).ContinueWith(() => MessageBox.Show(antecendent.Result.ToString()); // Antecedent result feeds into continuation
}

Upvotes: 2

Panagiotis Kanavos
Panagiotis Kanavos

Reputation: 131334

This isn't about multithreading per se. Parallel and PLINQ use multiple threads including the current one to process data in parallel. If you want to avoid blocking you'll have to execute your PLINQ query inside a background task, eg by using Task.Run:

private async void btnMultiThread_Click(object sender, RoutedEventArgs e)
{
    var numbers = Enumerable.Range(2, range - 1);
    var totalPrimes = await Task.Run(()=>
        numbers
        .AsParallel()
        .Where(n => Enumerable.Range(2, (int)Math.Sqrt(n) - 1).All(i => n % i != 0))
        .Count());

    MessageBox.Show(totalPrimes.ToString());
}

or to make this a bit cleaner:

private async void btnMultiThread_Click(object sender, RoutedEventArgs e)
{
    var numbers = Enumerable.Range(2, range - 1);
    var totalPrimes = await Task.Run(()=>CalculatePrimes(numbers));
    MessageBox.Show(totalPrimes.ToString());
}

private int CalculatePrimes(IEnumerable<int> numbers)
{
    return numbers
          .AsParallel()
          .Where(n => Enumerable.Range(2, (int)Math.Sqrt(n) - 1).All(i => n % i != 0))
          .Count());
}

Upvotes: 5

Related Questions