Последовательность рабочего процесса задач неверна

StackOverflow https://stackoverflow.com/questions/4405008

Вопрос

С приведенным ниже кодом окончательные обновления пользовательских интерфейсов, сделанные в финале, никогда не проводится. Я думаю, что это из-за ожидания () у меня есть в конце.

Причина, по которой я делаю это потому, что без ожидания метод вернет IDatataProvider до того, как его закончен, построенный на заднем плане.

Может кто-нибудь помочь мне получить это правильно?

Ваше здоровье,
Ягода

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

====

просто быть чистым

У меня нет проблем разговаривать с нити UI. Первый продолжается с обновлениями UI просто отлично. Беда у меня есть время последнего обновления UI и возврата поставщика данных.

Я прокомментировал некоторые из кода, чтобы уменьшить уровень шума в TIS POST и сосредоточиться на секвенировке задач.

====

ОК, рабочий код

        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);
    }
Это было полезно?

Решение

Обновление ниже
Этот код не выглядит так, как правило, для меня TPL. Похоже, может быть, хорошее использование для фонара вместо этого!

В любом случае, обновления, вероятно, не имеют место, потому что вы не можете обновлять пользовательский интерфейс из отдельного потока - вам нужно запустить обновление в потоке пользовательского интерфейса. Вы должны использовать диспетчеров для этого (http://stackoverflow.com/questions/303116/system-windows-threading-dispatcher-and-winforms содержит информацию как для WPF, так и для WinForms)

Обновлять:

Поэтому я, очевидно, упустил какой-то код, так что вот пересмотренный ответ. Прежде всего, Николас правильно - .Continuewith возвращает новую задачу (http://msdn.microsoft.com/en-us/library/dd270696.aspx). Так вместо

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

Вы, вероятно, хотите создать новую задачу, а затем сделать все ContinueWith() звонки и назначают задачу, а затем звонить .Start() на задачу. Что-то типа:

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

Тем не менее, есть недостаток в дизайне, чтобы начать с (как я вижу это)! Вы пытаетесь запустить этот код Async, Wihch - это почему вы используете потоки и TPL. Тем не менее, вы звоните buildProvider.Wait(); На ui поток, которая блокирует нить UI, пока эта задача не завершится! Кроме вопроса о перекраске пользовательского интерфейса в ContinueWith() Хотя нить пользовательского интерфейса заблокирована, здесь нет никакой пользы для многопоточничества здесь, так как вы блокируете нить UI (основной нет-нет). Что вы, вероятно, хотите сделать, это придерживаться Bind()-Ну внутри продолжения или что-то, чтобы вам не нужно звонить Wait() и заблокировать нить интерфейса.

Мои $ 0,02 - это то, что если вы ожидаете, что запрос займет много времени, что вы действительно хотите, это 2 потока (или задачи в TPL) - один для выполнения запроса и один, чтобы обновить интервалы UI с помощью статуса. Если вы не ожидаете, что это займет так много времени, я думаю, что вы просто хотите, чтобы выиграть одну нить (задачу), чтобы запросить, а затем обновить пользовательский интерфейс, когда это сделано. Я бы, вероятно, сделал это через BackgroundWorker. Отказ TPL был построен для управления множеством задач и продолжений и такими, но кажется излишним для такого рода вещей - я думаю, что вы могли бы сделать это, используя фоновый механизм в гораздо меньший код. Но вы упомянуете, что хотите использовать TPL, что в порядке, но вам будет немного переделать это немного, чтобы он на самом деле работает на заднем плане!

PS - ты, вероятно, хотел поставить Console.WriteLine("Exception handled. Let's move on."); внутри catch

Другие советы

Я немного туманно, но в прошлый раз я использовал TPL, я нашел с толку. ContinueWith() Возвращает новый Task пример. Так что вам нужно назначить второй ContinueWith() привести к новой переменной, скажем var continuedTask = builderProvider.ContinueWith(...), и затем измените последний, чтобы ссылаться continuedTask.ContinueWith() вместо buildProvider.ContinueWith(). Отказ потом Wait() в последнее время Task.

Надеюсь, это поможет!

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top