web-dev-qa-db-fra.com

C # Threading/Async: Exécution d’une tâche en arrière-plan pendant que l’interface utilisateur est interactable

Après avoir regardé Async/Await et Threading, je ne suis toujours pas sûr de la bonne façon de l'appliquer à ma situation. Quelle que soit la variante que j'essaie d'utiliser, l'interface utilisateur reste bloquée, car je ne semble pas appeler la fonction souhaitée de manière asynchrone. De plus, il se peut que j'aie besoin de thread pour ma solution. 

Ce que j'essaie de faire: J'ai une application WPF sur laquelle il y a un bouton sur lequel je voudrais démarrer une opération qui permet toujours une interaction avec le programme, via l'interface utilisateur ou autrement. Une fois qu'une condition remplie qui est déterminée en dehors de cette fonction est remplie, la fonction doit se terminer. Cela me semble assez standard, mais j’ai le sentiment que je comprends mal quelque chose et je l’ai mal appliqué.

Ce que j'ai maintenant:  

private async void start_button_Click(object sender, RoutedEventArgs e)
{
    await StaticClass.MyFunction();
}

private void stop_button_Click(object sender, RoutedEventArgs e)
{
    StaticClass.stopFlag = true;
}

public static Task<int> myFunction()
{
    //Stuff Happens

    while(StaticClass.stopFlag == false)
        //Do Stuff

    //Stuff Happens

    return Task.FromResult(1) //I know this is bad, part of the reason I'm asking
}

J'espérais avoir des indications sur le point de savoir si j'aborde la question de la bonne façon et sur ce que je fais de mal.

15
Karoly S

Vous l'avez certainement mal implémenté. Vous retournez un Task<int>, mais seulement une fois que tout le travail a déjà été effectué .

Il me semble que vous devriez probablement juste avoir une méthode synchrone :

private static void MyFunction()
{
    // Loop in here
}

Puis lancez une tâche pour cela comme ceci:

Task task = Task.Run((Action) MyFunction);

Vous pouvez ensuite attendre cette tâche si vous le souhaitez - bien que dans l'exemple que vous avez donné, il ne sert à rien de le faire, car vous ne faites rien après la await de toute façon.

Je conviens également avec Reed que l’utilisation de CancellationToken serait plus propre qu’un drapeau statique ailleurs.

24
Jon Skeet

Vous avez mal compris.

public static Task<int> myFunction()
{
    //Stuff Happens

    while(StaticClass.stopFlag == false)
        //Do Stuff

    //Stuff Happens

    return Task.FromResult(1) //I know this is bad, part of the reason I'm asking
}

Tout ce code se produit toujours dans l'appel initial await StaticClass.MyFunction();, il ne rend jamais le contrôle à l'appelant. Ce que vous devez faire est de placer la partie boucle dans un thread séparé.

public static async Task myFunction()
{
    //Stuff Happens on the original UI thread

    await Task.Run(() => //This code runs on a new thread, control is returned to the caller on the UI thread.
    {
        while(StaticClass.stopFlag == false)
            //Do Stuff
    });

    //Stuff Happens on the original UI thread after the loop exits.
}
7
Scott Chamberlain

Au lieu d'essayer d'utiliser bool pour cela, vous devriez envisager d'utiliser le framework d'annulation géré intégré dans le framework.

En gros, vous construisez une CancellationTokenSource et transmettez une CancellationToken à votre méthode qui pourrait être utilisée pour gérer l'annulation.

Enfin, votre méthode actuelle ne sortira jamais du thread d'interface utilisateur. Vous devez utiliser Task.Run ou similaire pour déplacer la méthode vers le ThreadPool si vous ne voulez pas bloquer l'interface utilisateur.

3
Reed Copsey

Au lieu de cela, vous pouvez utiliser BackgroundWorker class pour faire le travail, votre interface restera interactive.

1
Eugene