1

我正在尝试为我们的一个程序编写一个窗口管理器。它应该向客户端显示一系列窗口,每个窗口询问不同的信息,然后在移动到下一个窗口之前处理该信息。可以动态添加或删除新窗口,因此可以使用窗口管理器。为了做到这一点,我有一个提供父窗口的主 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;
        }
    }

设置窗口管理器.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 线程。如果有什么不同,我将 Bcl 编译器组件与 .net 4.0 一起使用。这是一个大项目,我们还没有准备好迁移到 .net 4.5。

编辑:我对塞思克兰的回答的回复太长了,无法发表评论。

我不能使用 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;

        }));

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

我无法让这些方法中的任何一种发挥作用,最终走上了一条完全不同的路线。感谢您的建议,但我根本没有时间在这个项目上再搞乱他们了。

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 回答