Domanda

Con il codice qui sotto, gli aggiornamenti finali dell'interfaccia utente effettuati nel ContinueWith finale mai luogo. Penso che sia a causa del Wait () Ho alla fine.

La ragione per cui sto facendo questo perché, senza l'attesa, il metodo restituirà l'IDataProvider prima del suo essere finito costruito in background.

Può qualcuno aiutarlo ad ottenere questo diritto?

Saluti,
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;
    }

====

tanto per essere chiari

Io non ho problemi a parlare con il thread dell'interfaccia utente. La prima continua con aggiornamenti l'interfaccia utente più che bene. Il problema che sto avendo è il momento dell'ultimo aggiornamento dell'interfaccia utente e il ritorno del fornitore di dati.

ho commentato parte del codice per ridurre il livello di rumore in TIS postale e concentrarsi sul sequenziamento compito.

====

ok, il codice di lavoro

        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);
    }
È stato utile?

Soluzione

UPDATE seguente
Questo codice non sembra che garantisce TPL a me. Assomiglia forse un buon uso per un BackgroundWorker invece!

In entrambi i casi, gli aggiornamenti vengono probabilmente non avendo luogo perché non si può aggiornare l'interfaccia utente da un thread separato - è necessario eseguire l'aggiornamento sul thread dell'interfaccia utente. Si dovrebbe usare il Dispatcher per questo (http://stackoverflow.com/questions/303116/system-windows-threading-dispatcher-and-winforms contiene informazioni sia per WPF e WinForms)
Aggiornamento:

Quindi, ovviamente, mancava una parte del codice quindi ecco una risposta rivisto. Prima di tutto, Nicholas è corretta - .ContinueWith restituisce una nuova attività (http://msdn.microsoft.com/en-us/library/dd270696.aspx). Così, invece di

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

probabilmente si desidera creare una nuova attività e poi fare tutte le chiamate ContinueWith() e assegnare all'attività e quindi chiamare .Start() sul compito. Qualcosa di simile:

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

Tuttavia, v'è un difetto nella progettazione per cominciare (come la vedo io)! Stai cercando di eseguire questo asincrona del codice, wihch è il motivo per cui si sta utilizzando thread e TPL. Tuttavia, si sta chiamando buildProvider.Wait(); sul thread dell'interfaccia utente che blocca il thread UI fino Questo completa compito! A parte la questione della riverniciatura l'interfaccia utente nella ContinueWith() mentre il thread dell'interfaccia utente è bloccato, non c'è alcun beneficio per il multithreading qui quanto si sta bloccando il thread UI (un grande no-no). Quello che probabilmente vuole fare è attaccare il Bind()-zione all'interno di un ContinueWith o qualcosa in modo che non c'è bisogno di chiamare Wait() e bloccare il thread dell'interfaccia utente.

Il mio $ 0.02 è che se ci si aspetta la query di prendere un lungo periodo di tempo ciò che si vuole veramente è di 2 thread (o compiti in TPL) - uno per eseguire la query e uno per aggiornare l'interfaccia utente a intervalli con lo stato. Se non si prevede di prendere così tanto tempo credo che si desidera solo un singolo thread (Task) per query e quindi aggiornare l'interfaccia utente quando è fatto. Io probabilmente fare questo via BackgroundWorker. TPL è stata costruita per la gestione di un sacco di compiti e continuazioni e tale, ma sembra eccessivo per questo genere di cose - penso che si potrebbe fare con un BackgroundWorker in molto meno codice. Ma si parla che si desidera utilizzare TPL che va bene, ma si sta andando ad avere per rielaborare questo un po 'in modo che in realtà viene eseguito in background!

PS - probabilmente destinata a mettere la Console.WriteLine("Exception handled. Let's move on."); all'interno del catch

Altri suggerimenti

io sono un po 'confuso, ma l'ultima volta ho usato il TPL ho trovato confusione. ContinueWith() restituisce una nuova istanza Task. Quindi è necessario assegnare il secondo risultato ContinueWith() ad una nuova variabile, dire var continuedTask = builderProvider.ContinueWith(...), e quindi modificare l'ultimo a continuedTask.ContinueWith() di riferimento invece di buildProvider.ContinueWith(). Poi Wait() sull'ultimo Task.

Speranza che aiuta!

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top