C# WPF异步命令阻止用户界面
我刚开始使用C# WPF异步命令阻止用户界面,c#,wpf,multithreading,asynchronous,mvvm,C#,Wpf,Multithreading,Asynchronous,Mvvm,我刚开始使用Task.Run()和async,并等待使UI更具响应性,因此我可能没有正确地实现某些功能 我已经阅读了Stephen Cleary关于使用AsyncCommands的优秀文章,并使用了他的代码作为具有响应UI的基础,但当我运行代码时,它似乎仍然冻结(在函数完全完成之前,我无法移动窗口或与其他按钮交互) 我正在尝试执行一个通常需要5-10秒才能返回的搜索。下面是创建AsyncCommand的代码以及函数的作用 代码: public ICommand SearchCommand { 得
Task.Run()
和async,并等待使UI更具响应性,因此我可能没有正确地实现某些功能
我已经阅读了Stephen Cleary关于使用AsyncCommands的优秀文章,并使用了他的代码作为具有响应UI的基础,但当我运行代码时,它似乎仍然冻结(在函数完全完成之前,我无法移动窗口或与其他按钮交互)
我正在尝试执行一个通常需要5-10秒才能返回的搜索。下面是创建AsyncCommand
的代码以及函数的作用
代码:
public ICommand SearchCommand
{
得到
{
如果(_SearchCommand==null)
{
_SearchCommand=AsyncCommand.Create(()=>Search());
}
返回搜索命令;
}
}
专用异步任务搜索()
{
IEnumerable points=await SearchAsync(_CurrentPIServer,NameSearch,PointSourceSearch)。ConfigureAwait(false);
SearchResults.Clear();
foreach(点中的点)
{
SearchResults.Add(point.Name);
}
}
专用异步任务SearchAsync(字符串服务器、字符串名称搜索、字符串点源搜索)
{
{
PIServers KnownServers=新PIServers();
PIServer server=knownserver[server];
Connect();
返回wait Task.Run(()=>PIPoint.FindPIPoints(server,NameSearch,PointSourceSearch)).configurewait(false);
}
}
我认为问题在于我是如何将长时间运行的函数推到一个线程上的,它没有脱离UI线程,或者我对任务和异步/等待是如何完全脱离的理解
编辑1:
根据Stephen的回答,我更新了函数,但我没有看到UI响应方面的任何变化。我创建了第二个命令,执行相同的操作,并且在任何情况下都从UI获得相同的响应。代码现在如下所示
代码:
public ICommand SearchCommand
{
得到
{
如果(_SearchCommand==null)
{
_SearchCommand=AsyncCommand.Create(异步()=>
{
var results=wait Task.Run(()=>Search(_CurrentPIServer,NameSearch,PointSourceSearch));
SearchResults=新的ObservableCollection(results.Select(x=>x.Name));
});
}
返回搜索命令;
}
}
公共ICommand搜索命令2
{
得到
{
如果(_SearchCommand2==null)
{
_SearchCommand2=新的RelayCommand(()=>
{
var results=搜索(_CurrentPIServer,NameSearch,PointSourceSearch);
SearchResults=新的ObservableCollection(results.Select(x=>x.Name));
}
,()=>正确);
}
返回搜索命令2;
}
}
私有IEnumerable搜索(字符串服务器、字符串名称搜索、字符串点源搜索)
{
PIServers KnownServers=新PIServers();
PIServer server=knownserver[server];
Connect();
返回PIPoint.FindPIPoints(服务器、名称搜索、点源搜索);
}
我一定是错过了什么,但我不知道在这一点上是什么
编辑2:
在对什么花费了这么长时间进行了更多的调查之后,结果表明,在发现结果后对列表的迭代是挂起整个过程的原因。通过简单地更改
搜索
函数返回的内容,并让它在对象列表上进行迭代,可以使UI保持响应。我将Stephen的答案标记为正确,因为它处理了我的主要问题,即正确地将工作移出UI线程,我只是没有将实际耗时的工作移出。我的第一个猜测是,排到Task.Run
的工作相当快,延迟是由其他代码(例如,PIServer.Connect
)引起的
另一个值得注意的是,您在Search
中使用的ConfigureAwait(false)
更新了SearchResults
,我怀疑这是错误的。如果SearchResults
绑定到UI,那么更新时您应该在UI上下文中,因此不应该使用ConfigureAwait(false)
这就是说,有一个Task.Run
原则需要牢记:将Task.Run
推到调用堆栈的尽可能高的位置。一般的想法是Task.Run
应该用于调用同步方法,而不应该用于异步方法的实现(至少,不是打算重复使用的)
最后,请注意,async
本质上是功能性的。因此,作为副作用,返回结果比更新集合更自然
结合这些建议,生成的代码如下所示:
private IEnumerable<PIPoint> Search(string Server, string NameSearch, string PointSourceSearch)
{
PIServers KnownServers = new PIServers();
PIServer server = KnownServers[Server];
// TODO: If "Connect" or "FindPIPoints" are naturally asynchronous,
// then this method should be converted back to an asynchronous method.
server.Connect();
return PIPoint.FindPIPoints(server, NameSearch, PointSourceSearch);
}
public ICommand SearchCommand
{
get
{
if (_SearchCommand == null)
{
_SearchCommand = AsyncCommand.Create(async () =>
{
var results = await Task.Run(() =>
Search(_CurrentPIServer, NameSearch, PointSourceSearch));
SearchResults = new ObservableCollection<string>(
results.Select(x => x.Name));
});
}
return _SearchCommand;
}
}
private IEnumerable搜索(字符串服务器、字符串名称搜索、字符串点源搜索)
{
PIServers KnownServers=新PIServers();
PIServer server=knownserver[server];
//TODO:如果“Connect”或“FindPIPoints”自然是异步的,
//然后该方法应转换回异步方法。
Connect();
返回PIPoint.FindPIPoints(服务器、名称搜索、点源搜索);
}
公共ICommand SearchCommand
{
得到
{
如果(_SearchCommand==null)
{
_SearchCommand=AsyncCommand.Create(异步()=>
{
var results=等待任务。运行(()=>
搜索(_CurrentPIServer,NameSearch,PointSourceSearch));
SearchResults=新的ObservableCollection(
结果:选择(x=>x.Name));
});
}
返回搜索命令;
}
}
您的服务器是什么
public ICommand SearchCommand
{
get
{
if (_SearchCommand == null)
{
_SearchCommand = AsyncCommand.Create(async () =>
{
var results = await Task.Run(()=>Search(_CurrentPIServer, NameSearch, PointSourceSearch));
SearchResults = new ObservableCollection<string>(results.Select(x => x.Name));
});
}
return _SearchCommand;
}
}
public ICommand SearchCommand2
{
get
{
if (_SearchCommand2 == null)
{
_SearchCommand2 = new RelayCommand(() =>
{
var results = Search(_CurrentPIServer, NameSearch, PointSourceSearch);
SearchResults = new ObservableCollection<string>(results.Select(x => x.Name));
}
,()=> true);
}
return _SearchCommand2;
}
}
private IEnumerable<PIPoint> Search(string Server, string NameSearch, string PointSourceSearch)
{
PIServers KnownServers = new PIServers();
PIServer server = KnownServers[Server];
server.Connect();
return PIPoint.FindPIPoints(server, NameSearch, PointSourceSearch);
}
private IEnumerable<PIPoint> Search(string Server, string NameSearch, string PointSourceSearch)
{
PIServers KnownServers = new PIServers();
PIServer server = KnownServers[Server];
// TODO: If "Connect" or "FindPIPoints" are naturally asynchronous,
// then this method should be converted back to an asynchronous method.
server.Connect();
return PIPoint.FindPIPoints(server, NameSearch, PointSourceSearch);
}
public ICommand SearchCommand
{
get
{
if (_SearchCommand == null)
{
_SearchCommand = AsyncCommand.Create(async () =>
{
var results = await Task.Run(() =>
Search(_CurrentPIServer, NameSearch, PointSourceSearch));
SearchResults = new ObservableCollection<string>(
results.Select(x => x.Name));
});
}
return _SearchCommand;
}
}