0

我很难过,我需要你的帮助。

我有一个使用 ASYNC & AWAIT 调用 WEBAPI 的 WPF 应用程序,并且我的 UI 遇到了延迟。

这是我正在使用的代码...

HttpResponseMessage response = await WEBAPI.GetHttpClient().GetAsync("api/COStateType/GetStateTypesByCountryID/" + Constants.COUNTRY_ID_USA);  
response.EnsureSuccessStatusCode(); 
App.stateTypes = await response.Content.ReadAsAsync<List<coStateType>>(); 

GetHttpClient 代码...

 public static HttpClient GetHttpClient()
    {
        HttpClient client = new HttpClient();
        client.BaseAddress = new Uri(ConfigurationManager.AppSettings["ABS2.WEBAPI.Uri"]);
        client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
        return client;
    }

这里是主要问题...

在启动过程中,我会显示一个带有移动齿轮(动画)的启动画面。实现对 WEBAPI 的调用后,动画不再流畅。我已经读到使用 HttpClient 的 ASYNC 调用会导致 UI 阻塞,这就是我的动画所发生的情况。

这是问题...

反正有这个问题吗?我尝试将此调用包装在 BackgroundWorker 和/或 Task 中,但均未解决问题。是否有另一种与不会导致此 UI 阻塞的 WEBAPI 通信的方式?我做错了什么?

非常非常感谢您的任何帮助!!!

4

4 回答 4

2

@Stef7 和 @Panagiotis - 你们说得对,UI 线程被阻止了。我不确定我是否理解为什么异步调用会阻塞 UI 线程;我将不得不做更多的研究。

我已经尝试了一个后台线程,但是直到我在任务上实现了 ContinueWith 才解决了我的问题。

感谢您的帮助。

这是最终的代码。

public partial class Splash : Window
{
    private TypeBO boType = new TypeBO();
    private Stopwatch swTotal = new Stopwatch();
    private Stopwatch swData= new Stopwatch();

    public Splash()
    {
        InitializeComponent();
        swTotal.Start();
    }

    private void GetData()
    {
        Assembly assembly = Assembly.GetExecutingAssembly();
        lblMessage.Content = "Please Wait...";
        lblVersion.Content = "Version: " + assembly.GetName().Version.Major + "." + assembly.GetName().Version.Minor + "." + assembly.GetName().Version.Revision;

        swData.Start();

        var backgroundWorker = new BackgroundWorker();
        backgroundWorker.DoWork += this.OnBackgroundWorkerDoWork;
        backgroundWorker.RunWorkerCompleted += OnBackgroundWorkerRunWorkerCompleted;
        backgroundWorker.RunWorkerAsync();
    }

    private void OnBackgroundWorkerDoWork(object sender, DoWorkEventArgs e)
    {
        WEBAPI.GetHttpClient().GetAsync("api/COStateType/GetStateTypesByCountryID/"
            + Constants.COUNTRY_ID_USA).ContinueWith(r =>
            {
                r.Result.EnsureSuccessStatusCode();
                e.Result = r.Result.Content.ReadAsAsync<List<coStateType>>().Result;
            }).Wait();

        swData.Stop();
        System.Diagnostics.Debug.Write("Splash - DATA - Total Elapsed Time - " + swData.Elapsed + Environment.NewLine);
    }

    private void OnBackgroundWorkerRunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
    {
        var backgroundWorker = sender as BackgroundWorker;
        backgroundWorker.DoWork -= this.OnBackgroundWorkerDoWork;
        backgroundWorker.RunWorkerCompleted -= OnBackgroundWorkerRunWorkerCompleted;

        App.stateTypes = e.Result as List<coStateType>;

        coStateType pleaseSelectStateType = new coStateType() { State_ID = 0, State_Nm = "Select One", Country_ID = 1 };
        App.stateTypes.Insert(0, pleaseSelectStateType);

        sbHideState.Completed += (snd, eva) =>
        {
            //Pre-Load User Controls
            Assembly assembly = this.GetType().Assembly;
            UserControl ucBHSearch = (UserControl)assembly.CreateInstance(string.Format("{0}.BHSearch", "ABS2.WPF.ADMIN.UserControls.BusinessHierarchy"));
            UserControl ucBHDetails = (UserControl)assembly.CreateInstance(string.Format("{0}.BHDetails", "ABS2.WPF.ADMIN.UserControls.BusinessHierarchy"));
            App.userControls.Add("BHD", ucBHDetails);
            App.userControls.Add("BHS", ucBHSearch);

            Login winLogin = new Login();
            winLogin.Show();

            swTotal.Stop();
            System.Diagnostics.Debug.Write("Splash - UI - Total Elapsed Time - " + swTotal.Elapsed + Environment.NewLine + Environment.NewLine);
            swTotal.Reset();
            this.Close();
        };
        sbHideState.Begin(gridSplash);
    }

    private void Window_Loaded_1(object sender, RoutedEventArgs e)
    {
        lblMessage.Content = "";
        lblVersion.Content = "";
        sbDefaultState.Begin(gridSplash);
        sbRotateState.Begin(gridSplash);           
    }

    private void window_ContentRendered(object sender, EventArgs e)
    {
        sbShowState.Completed += (snd, eva) =>
        {
            GetData();
        };
        sbShowState.Begin(gridSplash);
    }
}
于 2012-12-19T13:31:44.630 回答
2

您需要使用“任务”函数来调用异步。这不会阻塞您的 GUI 主线程,您也不需要使用后台工作线程或线程。“任务”,“异步”,“等待”一起去。

于 2015-03-19T21:43:32.623 回答
1

await完成等待时,将在原始上下文(线程)中继续执行,在您的情况下是 UI 线程。

这允许您的代码在不使用调度程序的情况下访问 UI 控件。这意味着在最后一次等待之后的任何昂贵代码实际上都将在 UI 线程上运行。

如果您希望您的代码继续在单独的线程上运行,您应该在调用 await 之后添加 .ConfigureAwait(false),即。

await response.Content.ReadAsAsync<List<coStateType>>().ConfigureAwait(false);

在这种情况下,您应该注意使用 Dispatcher 或 SynchronizationContext 方法Post / Send访问 UI 控件,例如。

SynchronizationContext.Current.Post(_=>progressBar.Value+=5,null);
于 2012-12-18T08:27:37.287 回答
1

您发布的所有代码都在 UI 线程中执行。异步功能可以帮助您在 UI 线程无事可做时(在 Web 请求期间)不会阻塞它,但是如果您让 UI 线程忙于太多代码,您的动画总是会出现问题。

我会尝试将所有与 Web 相关的代码移动到后台工作程序中,并仅将简短的 UI 更新发布到 UI 线程。由于您写道您已经尝试过后台工作人员,因此我不得不假设 UI 更新花费的时间太长。

如果您有性能分析器,请分析 UI 线程的繁忙时间以获得更好的图像。

于 2012-12-18T15:57:11.860 回答