Question

Avec le code ci-dessous, les mises à jour de l'interface utilisateur finale faites dans la ContinueWith finale ne se déroulent. Je pense qu'il est à cause de l'attente () je à la fin.

La raison pour laquelle je fais cela est dû au fait sans attendre, la méthode retourne la IDataProvider avant son fini d'être construit en arrière-plan.

Quelqu'un peut-il me aider à obtenir ce droit?

Cheers,
Berryl

        private IDataProvider _buildSQLiteProvider()           
    {

        IDataProvider resultingDataProvider = null;
        ISession session = null;

        var watch = Stopwatch.StartNew();

        var uiContext = TaskScheduler.FromCurrentSynchronizationContext();

        // get the data
        var buildProvider = Task.Factory
            .StartNew(
                () =>
                    {
                        // code to build it
                    });

        // show some progress if we haven't finished
        buildProvider.ContinueWith(
            taskResult =>
            {
                // show we are making progress;
            },
            CancellationToken.None, TaskContinuationOptions.None, uiContext);

        // we have data: reflect completed status in ui 
        buildProvider.ContinueWith(
            dataProvider =>
            {
                // show we are finished;
            },
            CancellationToken.None, TaskContinuationOptions.OnlyOnRanToCompletion, uiContext);

        try {
            buildProvider.Wait();
        }

        catch (AggregateException ae)
        {
            foreach (var e in ae.InnerExceptions)
                Console.WriteLine(e.Message);
        }
        Console.WriteLine("Exception handled. Let's move on.");

        CurrentSessionContext.Bind(session);

        return resultingDataProvider;
    }

====

juste pour être clair

Je ne parviens pas à parler au fil ui. La première continue avec des mises à jour de l'interface très bien. Le problème que j'ai est le moment de la dernière mise à jour ui et le retour du fournisseur de données.

Je commentais sur une partie du code pour réduire le niveau de bruit en post tis et se concentrer sur l'enchaînement des tâches.

====

ok, code de travail

        private void _showSQLiteProjecPicker()           
    {

        var watch = Stopwatch.StartNew();
        var uiScheduler = TaskScheduler.FromCurrentSynchronizationContext();
        ISession session = null;

        // get the data
        var buildProvider = Task.Factory.StartNew(
                () =>
                {
                    var setProgress = Task.Factory.StartNew(
                        () =>
                        {
                            IsBusy = true;
                            Status = string.Format("Fetching data...");
                        },
                        CancellationToken.None, TaskCreationOptions.None, uiScheduler);

                    var provider = new SQLiteDataProvider();
                    session = SQLiteDataProvider.Session;
                    return provider;
                });


        buildProvider.ContinueWith(
            buildTask =>
                {
                    if(buildTask.Exception != null) {
                        Console.WriteLine(buildTask.Exception);
                    }
                    else {
                        Check.RequireNotNull(buildTask.Result);
                        Check.RequireNotNull(session);

                        _updateUiTaskIsComplete(watch);

                        CurrentSessionContext.Bind(session);
                        var provider = buildTask.Result;
                        var dao = provider.GetActivitySubjectDao();
                        var vm = new ProjectPickerViewModel(dao);
                        _showPicker(vm);
                    }
                },
            CancellationToken.None, TaskContinuationOptions.OnlyOnRanToCompletion, uiScheduler);
    }
Était-ce utile?

La solution

Mise à jour ci-dessous Ce code ne ressemble pas à elle justifie TPL pour moi. On dirait que peut-être un bon usage pour une place BackgroundWorker!

De toute façon, les mises à jour ne sont probablement pas lieu parce que vous ne pouvez pas mettre à jour l'interface utilisateur à partir d'un thread séparé - vous devez exécuter la mise à jour sur le thread d'interface utilisateur. Vous devez utiliser le répartiteur pour cette (http://stackoverflow.com/questions/303116/system-windows-threading-dispatcher-and-winforms contient des informations pour les WPF et WinForms)
Mise à jour:

Je évidemment manqué une partie du code voici donc une réponse révisée. Tout d'abord, Nicholas est correct - .ContinueWith retourne une nouvelle tâche (http://msdn.microsoft.com/en-us/library/dd270696.aspx). Ainsi, au lieu de

var result = Task.Factory.StartNew(...);
result.ContinueWith(...);

vous voulez probablement créer une nouvelle tâche et faire tous les appels ContinueWith() et attribuer à la tâche, puis appelez .Start() la tâche. Quelque chose comme:

var task = new Task(...).ContinueWith(...);
task.Start();

Cependant, il y a une faille dans la conception pour commencer (comme je le vois)! Vous essayez d'exécuter ce code async, wihch est la raison pour laquelle vous utilisez des fils et TPL. Cependant, vous appelez buildProvider.Wait(); sur le thread d'interface utilisateur qui bloque le thread d'interface utilisateur jusqu'à ce que la tâche terminée! Mis à part la question de repeindre l'interface utilisateur dans le ContinueWith() alors que le thread d'interface utilisateur est bloqué, il n'y a aucun avantage à multithreading ici puisque vous bloquer le thread d'interface utilisateur (un grand non-non). Ce que vous voulez sans doute faire est de coller le Bind()-ing dans un ContinueWith ou quelque chose afin que vous ne devez pas appeler Wait() et bloquer le thread d'interface utilisateur.

Mon 0,02 $ est que si vous vous attendez à la requête de prendre beaucoup de temps ce que vous voulez vraiment est 2 fils (ou tâches TPL) - un pour exécuter la requête et une mise à jour de l'interface utilisateur à intervalles avec le statut. Si vous ne vous attendez pas à prendre si longtemps que je pense que vous voulez juste un seul thread (tâche) pour interroger et mettre à jour l'interface utilisateur quand il est fait. Je serais probablement le faire via BackgroundWorker. TPL a été construit pour gérer de nombreuses tâches et continuations et tel, mais semble trop pour ce genre de chose - je pense que vous pouvez le faire en utilisant un BackgroundWorker dans beaucoup moins de code. Mais vous mentionnez que vous voulez utiliser TPL ce qui est bien, mais vous allez devoir retravailler cela un peu pour qu'il fonctionne en fait en arrière-plan!

PS - vous avez probablement voulu dire de mettre le Console.WriteLine("Exception handled. Let's move on."); à l'intérieur du catch

Autres conseils

Je suis un peu brumeux, mais la dernière fois je l'TPL je l'ai trouvé confus. ContinueWith() retourne une nouvelle instance de Task. Donc, vous devez attribuer le deuxième résultat de ContinueWith() à une nouvelle variable, disons var continuedTask = builderProvider.ContinueWith(...), puis modifiez le dernier à faire référence continuedTask.ContinueWith() au lieu de buildProvider.ContinueWith(). Puis Wait() le dernier Task.

L'espoir qui aide!

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top