我很困惑,我需要你的帮助。
我有一个使用 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的异步调用会导致UI阻塞,并且认为这就是我的动画正在发生的事情。
这是问题...
这个问题有办法吗? 我尝试将此调用包装在后台工作者和/或任务中,但都没有解决问题。 有没有另一种与 WEBAPI 通信的方法不会导致此 UI 阻塞? 我做错了什么?
非常感谢您的帮助!!
@Stef7 & @Panagiotis - 你们说得对,UI 线程被阻止了。 我不确定我是否理解为什么异步调用会阻止 UI 线程;我将不得不做更多的研究。
我已经尝试过后台线程,但在我在任务上实施继续之前,这并没有解决我的问题。
谢谢你的帮助。
这是最终代码。
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);
}
}
使用"Task"函数来调用Async。 这不会阻止您的 GUI 主线程,您也不需要使用后台工作线程或线程。"任务"、"异步"、"等待"一起进行。
当 await 完成等待时,在原始上下文(线程)中继续执行,在您的情况下是 UI 线程。
这允许代码在不使用调度程序的情况下访问 UI 控件。这意味着最后一个等待之后的任何昂贵代码实际上都将在 UI 线程上运行。
如果希望代码继续在单独的线程上运行,则应添加 。ConfigureAwait(false) 在调用后等待,即。
await response.Content.ReadAsAsync<List<coStateType>>().ConfigureAwait(false);
在这种情况下,您应该注意使用调度程序或同步上下文方法发布/发送来访问 UI 控件,例如。
SynchronizationContext.Current.Post(_=>progressBar.Value+=5,null);
您发布的所有代码都在 UI 线程中执行。异步功能可帮助您在 UI 线程无事时(在 Web 请求期间)不阻塞它,但是如果您让 UI 线程忙于过多的代码,您的动画将始终出现问题。
我会尝试将所有与 Web 相关的代码移动到后台工作线程中,并仅向 UI 线程发布简短的 UI 更新。由于您已经尝试了后台工作线程,因此我必须假设 UI 更新需要太长时间。
如果您有性能探查器,请分析 UI 线程的繁忙时间以获得更好的图片。