等待不能正常工作

时间:2013-08-08 14:58:26

标签: c# asynchronous async-await

我正在尝试为我们的某个程序编写一个窗口管理器。它应该向客户端显示一系列窗口,每个窗口询问不同的信息,然后在移动到下一个窗口之前处理该信息。可以动态添加或删除新窗口,因此窗口管理器。为了做到这一点,我有一个提供父窗口的主shell。每个子窗口作为内容插入父窗口。当单击下一个按钮时,孩子应该处理屏幕上的信息,然后让父母知道它可以移动到下一个窗口。因为某些窗口的处理时间可能长达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回答的回答太长,无法发表评论。

我不能'使用Task.Run,​​因为我们还没有使用.Net 4.5库。但是,它让我看着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;

        }));

那么如何从任务中获取返回值?

我无法使这些方法中的任何一种工作,最终走向完全不同的路线。谢谢你的建议,但我没有时间在这个项目上再乱用它们。

1 个答案:

答案 0 :(得分:1)

使用await和async关键字本身并不会使代码异步运行。通过调用异步方法,它可以正常运行。唯一真正的区别是它的结果将作为Task对象返回。我怀疑在你的情况下,因为我没有明确地看到工作被分拆到另一个线程或以后排队等待,所以在你的处理方法返回之前,这个Task对象不会返回,并且它已被标记为已完成,因此,您的等待和异步关键字实际上无效。

考虑通过

包装实际上想要在任务中异步运行的代码
return Task.Run(() => { yourLongProcessingMethodsHere });

这将运行异步传递的代码,然后返回当前线程,同时返回当前正在执行的显式Task对象。

因此,简而言之,async / await只会对你明确编写的方法有好处,并且可以将自己的线程分离出去,或者将事件排队到在以后运行,以标记任务完成。

有关async / await的更多信息,请参阅:http://msdn.microsoft.com/en-us/library/vstudio/hh191443.aspx