为什么我的异步任务会在 GUI 中冻结 AI 动画?
Why is my asynchronous task freezing AI animations in the GUI?
我的代码看起来很像下面这样:
namespace CloudKey
{
/// <summary>
/// Interaction logic for Page1.xaml
/// </summary>
public partial class Page1 : Page
{
public Page1()
{
InitializeComponent();
}
private async void button_Click(object sender, RoutedEventArgs e)
{
//Begin Loading animation
Loading.Visibility = Visibility.Visible;
//Run Task
await Task.Run(() => LoginCheck());
}
async void LoginCheck()
{
await Dispatcher.InvokeAsync(
() =>
{
InitialSessionState iss = InitialSessionState.CreateDefault();
StringBuilder ss = new StringBuilder();
ss.AppendLine("some code here")
using (Runspace runspace = RunspaceFactory.CreateRunspace(iss))
{
Collection<PSObject> results = null;
try
{
runspace.Open();
Pipeline pipeline = runspace.CreatePipeline();
pipeline.Commands.AddScript(ss.ToString());
results = pipeline.Invoke();
}
catch (Exception ex)
{
results.Add(new PSObject((object)ex.Message));
}
finally
{
runspace.Close();
Loading.Visibility = Visibility.Hidden;
if //some stuff
{
//do some things
}
else
{
//do some other things
}
}
}
});
}
}
}
我也试过了
Async void LoginCheck()
{
await Dispatcher.Invoke (() => {//Stuff});
}
结果相同。我实际上不确定两者之间的区别是什么...
无论哪种方式,任务都能正确运行,但动画会在任务开始后立即停止。 :/ 我应该怎么做才能让加载动画按照我想要的方式工作?
编辑:
我应该补充一点,我试图删除
await Dispatcher.InvokeAsync(
() =>{});
并保留函数的其余部分,但是,这样做时出现以下错误:
The calling thread cannot access this object because a different thread owns it.
我认为只需要删除
await Dispatcher.InvokeAsync(() => { };
围绕代码。查看 Dispatcher.InvokeAsync 的文档,它说 "Executes the specified delegate asynchronously on the thread the Dispatcher is associated with." (source) 链接到页面的 Dispatcher 与页面的线程关联,因此它在 UI 上执行代码线程,仍然。删除周围的函数调用应该使代码在不同的线程上简单地 运行 因为你已经在不同的线程上调用 Task.Run() 到 运行 它。
编辑:遗漏了加载更改的部分,该部分由 UI 线程拥有。这是一个更好地说明问题的实际代码示例:
async void LoginCheck()
{
InitialSessionState iss = InitialSessionState.CreateDefault();
StringBuilder ss = new StringBuilder();
ss.AppendLine("some code here")
using (Runspace runspace = RunspaceFactory.CreateRunspace(iss))
{
Collection<PSObject> results = null;
try
{
runspace.Open();
Pipeline pipeline = runspace.CreatePipeline();
pipeline.Commands.AddScript(ss.ToString());
results = pipeline.Invoke();
}
catch (Exception ex)
{
results.Add(new PSObject((object)ex.Message));
}
finally
{
runspace.Close();
Dispatcher.InvokeAsync(() => {
Loading.Visibility = Visibility.Hidden;
});
if //some stuff
{
//do some things
}
else
{
//do some other things
}
}
}
}
因为 Loading.Visibility 是一个 UI 元素,它属于 UI 线程。因此,围绕 Dispatcher.Invoke() 的调用将更改 UI 线程的值,同时仍在另一个线程上执行 db 调用,如上所述。
我的代码看起来很像下面这样:
namespace CloudKey
{
/// <summary>
/// Interaction logic for Page1.xaml
/// </summary>
public partial class Page1 : Page
{
public Page1()
{
InitializeComponent();
}
private async void button_Click(object sender, RoutedEventArgs e)
{
//Begin Loading animation
Loading.Visibility = Visibility.Visible;
//Run Task
await Task.Run(() => LoginCheck());
}
async void LoginCheck()
{
await Dispatcher.InvokeAsync(
() =>
{
InitialSessionState iss = InitialSessionState.CreateDefault();
StringBuilder ss = new StringBuilder();
ss.AppendLine("some code here")
using (Runspace runspace = RunspaceFactory.CreateRunspace(iss))
{
Collection<PSObject> results = null;
try
{
runspace.Open();
Pipeline pipeline = runspace.CreatePipeline();
pipeline.Commands.AddScript(ss.ToString());
results = pipeline.Invoke();
}
catch (Exception ex)
{
results.Add(new PSObject((object)ex.Message));
}
finally
{
runspace.Close();
Loading.Visibility = Visibility.Hidden;
if //some stuff
{
//do some things
}
else
{
//do some other things
}
}
}
});
}
}
}
我也试过了
Async void LoginCheck()
{
await Dispatcher.Invoke (() => {//Stuff});
}
结果相同。我实际上不确定两者之间的区别是什么...
无论哪种方式,任务都能正确运行,但动画会在任务开始后立即停止。 :/ 我应该怎么做才能让加载动画按照我想要的方式工作?
编辑:
我应该补充一点,我试图删除
await Dispatcher.InvokeAsync(
() =>{});
并保留函数的其余部分,但是,这样做时出现以下错误:
The calling thread cannot access this object because a different thread owns it.
我认为只需要删除
await Dispatcher.InvokeAsync(() => { };
围绕代码。查看 Dispatcher.InvokeAsync 的文档,它说 "Executes the specified delegate asynchronously on the thread the Dispatcher is associated with." (source) 链接到页面的 Dispatcher 与页面的线程关联,因此它在 UI 上执行代码线程,仍然。删除周围的函数调用应该使代码在不同的线程上简单地 运行 因为你已经在不同的线程上调用 Task.Run() 到 运行 它。
编辑:遗漏了加载更改的部分,该部分由 UI 线程拥有。这是一个更好地说明问题的实际代码示例:
async void LoginCheck()
{
InitialSessionState iss = InitialSessionState.CreateDefault();
StringBuilder ss = new StringBuilder();
ss.AppendLine("some code here")
using (Runspace runspace = RunspaceFactory.CreateRunspace(iss))
{
Collection<PSObject> results = null;
try
{
runspace.Open();
Pipeline pipeline = runspace.CreatePipeline();
pipeline.Commands.AddScript(ss.ToString());
results = pipeline.Invoke();
}
catch (Exception ex)
{
results.Add(new PSObject((object)ex.Message));
}
finally
{
runspace.Close();
Dispatcher.InvokeAsync(() => {
Loading.Visibility = Visibility.Hidden;
});
if //some stuff
{
//do some things
}
else
{
//do some other things
}
}
}
}
因为 Loading.Visibility 是一个 UI 元素,它属于 UI 线程。因此,围绕 Dispatcher.Invoke() 的调用将更改 UI 线程的值,同时仍在另一个线程上执行 db 调用,如上所述。