首页 文章

WPF> WEBAPI> HttpClient> AWAIT = UI延迟

提问于
浏览
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中包装此调用,但都没有解决问题 . 是否有另一种与WEBAPI通信的方式,不会导致此UI阻塞?我做错了什么?

很多,非常感谢任何和所有的帮助!

4 回答

  • 1

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

    我已经尝试了一个后台线程但直到我在Task上实现了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);
        }
    }
    
  • 2

    当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);
    
  • 1

    您发布的所有代码都在UI线程中执行 . 异步功能可以帮助您在没有任何操作(Web请求期间)时阻止UI线程,但如果您让UI线程忙于处理太多代码,则动画将始终出现问题 .

    我会尝试将所有与Web相关的代码移动到后台工作程序中,并仅向UI线程发布简短的UI更新 . 既然你写过你已经尝试了后台工作者,我必须假设UI更新花了太长时间 .

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

  • 1

    你需要使用“任务”功能来调用异步 . 这不会阻止您的GUI主线程,也不需要使用后台工作程序或线程 . “任务”,“异步”,“等待”一起去 .

相关问题