1

私たちのプログラムの 1 つのウィンドウ マネージャーを作成しようとしています。クライアントに一連のウィンドウを表示し、各ウィンドウがさまざまな情報を要求し、その情報を処理してから次のウィンドウに移動する必要があります。新しいウィンドウは動的に追加または削除できるため、ウィンドウ マネージャーです。これを行うために、親ウィンドウを提供するメイン シェルがあります。各子ウィンドウは、コンテンツとして親ウィンドウに挿入されます。次のボタンをクリックすると、子供は画面上の情報を処理し、次のウィンドウに移動できることを親に知らせる必要があります。一部のウィンドウでは処理に最大 30 秒かかる場合があるため、この処理の進行中にビジー インジケーターを表示したいと考えています。親ビューで Telerik RadBusyIndi​​cator を使用してそれを達成しています。ビジー インジケーターを適切に表示するために、await/async を使用して OnNext メソッドを非同期で呼び出そうとしています。OnNext メソッドが呼び出されて処理が行われていますが、処理中に UI スレッドがロックされており、ビジー インジケーターが表示されていません。私のコードの関連部分は次のとおりです。

ShellViewModel.cs (親ウィンドウのビュー モデル):

    protected async void NextAction()
    {
                    //This is the property that is bound to the RadBusyIndicator to indicate if it should show or not
        IsBusy = true;

                    //windowManager is the class that tells the child window to start processing and provides the parent window with the next view
        bool? retVal = await windowManager.OnNext();

        if (retVal == true)
        {
            GetCurrentView();
        }
        else if (retVal == null)
        {
            SetupApp.IsRestarting = true;
            GetCurrentView();
            SetupApp.Config.RestartPoint = windowManager.CurrentViewCounter;
            File.WriteAllText("Config.xml", SetupConfig.Serialize(SetupApp.Config));
            System.Diagnostics.Process.Start(Application.ResourceAssembly.Location);
            Application.Current.Shutdown();
        }

        IsBusy = false;
    }

    private async void GetCurrentView()
    {
        IsBusy = true;

        SetupViewInit retVal = await windowManager.InitializeView();

        if (retVal == SetupViewInit.CloseApp)
        {
            RaiseCloseRequest();
            IsBusy = false;
            return;
        }

        IsBusy = false;

        Content = windowManager.CurrentView as FrameworkElement;

        OnPropertyChanged("CanNext");
        OnPropertyChanged("CanBack");
        OnPropertyChanged("NextToolTip");

        if (windowManager.IsFinalView)
        {
            CancelText = "Close";
            SetupApp.IsComplete = true;
        }
    }

SetupWindowManager.cs:

    public async Task<bool?> OnNext()
    {
                    //calls into the current view model to begin processing
        bool? retVal = CurrentViewModel.OnNext();

        if (retVal == true)
            CurrentViewCounter++;

        return retVal;
    }

            //Called from GetCurrentView to set the current view model and perform any initialization that view model might need
    public async Task<SetupViewInit> InitializeView()
    {

        CurrentView = currentViewList[CurrentViewCounter];
        CurrentViewModel.FireNextAction += FireNextAction;
        CurrentViewModel.PropertyChanged += PropertyChanged;
        SetupViewInit retVal = CurrentViewModel.Init();

        if (retVal == SetupViewInit.NextScreen)
        {
            CurrentViewCounter++;
            retVal = await InitializeView();
        }
        else if (retVal == SetupViewInit.TypeChange)
        {
            SetupType = SetupApp.Config.SetupType;
            MachineType = SetupApp.Config.MachineType;
            GetViewList();
            retVal = await InitializeView();
        }

        return retVal;
    }

ここでも、すべてのコードが適切に実行されています。コードは UI スレッドをロックしているだけです。違いがある場合は、.net 4.0 で Bcl コンパイラ コンポーネントを使用しています。これは大きなプロジェクトであり、まだ .net 4.5 に移行する準備ができていません。

編集: Sethcran の回答に対する私の回答は、コメントするには長すぎます。

.Net 4.5 ライブラリをまだ使用していないため、Task.Run を使用できません。しかし、Task.Factory.StartNew を見てみました。以下に示すようにコードを変更したところ、コメントに示されている問題が発生しました。

        Task.Factory.StartNew<SetupViewInit>(new Func<SetupViewInit>(async () =>
        {
            CurrentView = currentViewList[CurrentViewCounter];
            CurrentViewModel.FireNextAction += FireNextAction;
            CurrentViewModel.PropertyChanged += PropertyChanged;
            SetupViewInit retVal = CurrentViewModel.Init();

            if (retVal == SetupViewInit.NextScreen)
            {
                CurrentViewCounter++;
                retVal = await InitializeView();
            }
            else if (retVal == SetupViewInit.TypeChange)
            {
                SetupType = SetupApp.Config.SetupType;
                MachineType = SetupApp.Config.MachineType;
                GetViewList();
                retVal = await InitializeView();
            }
                            //On the following line, VS shows an "Unexpected return value" error
            return retVal;

        }));

では、タスクからの戻り値を取得するにはどうすればよいでしょうか?

これらの方法のいずれも機能させることができず、まったく別のルートに行き着きました。あなたの提案に感謝しますが、私はこのプロジェクトをこれ以上いじる時間がありません.

4

1 に答える 1

1

await および async キーワードを使用しても、それ自体ではコードが後で非同期に実行されるわけではありません。非同期メソッドを呼び出すことで、通常どおり実行されます。唯一の実際の違いは、結果が Task オブジェクトとして返されることです。あなたの場合、作業が別のスレッドにスピンオフされたり、後でキューに入れられたりすることを明示的に確認していないため、この Task オブジェクトは処理メソッドが返されるまで返されず、既に完了としてマークされていると思われます、したがって、ここでの await および async キーワードは事実上何もしていません。

実際にタスク内で非同期に実行したいコードをラップすることを検討してください。

return Task.Run(() => { yourLongProcessingMethodsHere });

これにより、渡されたコードが非同期的に実行され、現在実行中の明示的な Task オブジェクトと同時に現在のスレッドに返されます。

つまり、要するに、async/await は、明示的に動作するように記述されたメソッドでのみ効果を発揮し、独自のスレッドをスピンオフして動作させるか、またはイベントをキューに入れて実行します。後日、タスクの完了をマークします。

async/await の詳細については、http: //msdn.microsoft.com/en-us/library/vstudio/hh191443.aspxを参照してください。

于 2013-08-08T15:12:32.323 に答える