سؤال

مع الكود أدناه ، لم يتم إجراء تحديثات واجهة المستخدم النهائية في الاستمرارية النهائية. أعتقد أنه بسبب الانتظار () لدي في النهاية.

السبب في أنني أفعل ذلك هو أنه بدون الانتظار ، ستعود الطريقة إلى idataprovider قبل أن يتم بناؤها في الخلفية.

هل يمكن لأي شخص مساعدتي في الحصول على هذا بشكل صحيح؟

هتافات،
بيري

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

====

فقط لأكون واضحا

لا أواجه مشكلة في التحدث إلى موضوع واجهة المستخدم. الأول يستمر مع تحديثات واجهة المستخدم على ما يرام. المشكلة التي أواجهها هي توقيت آخر تحديث واجهة المستخدم وإرجاع مزود البيانات.

لقد علقت بعض التعليمات البرمجية لتقليل مستوى الضوضاء في منشور TIS والتركيز على تسلسل المهام.

====

حسنًا ، رمز العمل

        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(); على سلسلة واجهة المستخدم التي تمنع مؤشر ترابط واجهة المستخدم حتى تكمل هذه المهمة! بصرف النظر عن مسألة إعادة طلاء واجهة المستخدم في ContinueWith() على الرغم من حظر موضوع واجهة المستخدم ، لا يوجد أي فائدة للترابطات المتعددة هنا لأنك تقوم بحظر سلسلة واجهة المستخدم (لا يوجد رقم كبير). ما تريد القيام به هو التمسك Bind()-داخل سلسلة متصلة أو شيء من هذا القبيل حتى لا تضطر إلى الاتصال Wait() وحظر موضوع واجهة المستخدم.

0.02 دولار هو أنه إذا كنت تتوقع أن يستغرق الاستعلام وقتًا طويلاً ، فإن ما تريده حقًا هو موضوعان (أو مهام في TPL)- أحدهما لأداء الاستعلام وواحد لتحديث واجهة المستخدم على فترات مع الحالة. إذا كنت لا تتوقع أن يستغرق الأمر وقتًا طويلاً ، فأعتقد فقط أنك تريد فقط موضوعًا واحدًا (مهمة) للاستعلام ثم تحديث واجهة المستخدم عند الانتهاء. ربما سأفعل هذا عبر BackgroundWorker. تم بناء TPL لإدارة الكثير من المهام والاستمرارية ، ولكن يبدو أن المبالغة في هذا النوع من الأشياء - أعتقد أنه يمكنك القيام بذلك باستخدام عامل خلفية في رمز أقل بكثير. لكنك ذكرت أنك تريد استخدام TPL وهو أمر جيد ، لكن سيتعين عليك إعادة صياغة هذا الأمر قليلاً بحيث يتم تشغيله بالفعل في الخلفية!

ملاحظة - ربما كنت تهدف إلى وضع 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