ShowDialog 中的 Catel async await 命令 - 死锁
Posted
技术标签:
【中文标题】ShowDialog 中的 Catel async await 命令 - 死锁【英文标题】:Catel async await command in ShowDialog - Deadlock 【发布时间】:2014-02-05 06:51:36 【问题描述】:使用库Сatel 最新版本(3.8.1 beta)。
如何在对话窗口中使用 TAP 方法?
示例。 在主 ViewModel 中调用方法
private bool ShowDialogWindow()
var typeFactory = TypeFactory.Default ;
var vm = typeFactory.CreateInstanceWithParametersAndAutoCompletion<LoginWindowViewModel>();
return _uiVisualizerService.ShowDialog(vm) ?? false;
在 LoginWindowViewModel 我有命令(也可以尝试 AsynchronousCommand),它被称为方法
public async Task<int> Test(string login, string password)
var a = await Task<int>.Factory.StartNew(() =>
using (var uow = new UnitOfWork<TSDbContext>())
var userRep = uow.GetRepository<IUserRepository>();
userRep.GetAll();
return 5;
);
a++;
return a;
我只有在关闭对话框窗口时才从等待的方法中得到结果。 锁出现在线
var uow = new UnitOfWork()
ConfigureAwait(false) - 无助于解决问题
当我删除 UnitOfWork - 方法有效
当我将方法代码更改为此 var d = TypeFactory.Default.CreateInstanceWithParameters(); 返回 5;
阻塞也复现就行TypeFactory...
根据服务,Catel 不允许出现在对话框中
【问题讨论】:
那么您的UnitOfWork
构造函数是做什么的?
github.com/Catel/Catel/tree/develop/src/…
你有没有调试过它挂在哪里?我在那里看不到任何明显的问题......
我不认为是锁。我认为这里的重点并不完全在于 unitofwork 作为访问配置或依赖解析的机制
你说“锁出现”-这表明它正在锁定(挂起)。那么,如果你在代码被锁定的情况下闯入代码,堆栈跟踪会是什么样子呢?我也不清楚这两个不同的代码 sn-ps 是如何相互关联的。
【参考方案1】:
注意:我编辑了这个答案,所以它包含了这个问题的答案。上一个答案包含一些提示,供主题启动者调查问题。
您在 MainViewModel 的 构造函数 中调用命令。请注意,我们从不建议您在构造函数中调用任何内容。为此,我们有 Initialize 方法。
原因是您使用 TypeFactory 构造了 MainViewModel(Catel 会为您做这件事)。然后在该线程中执行的同一(异步)命令中,您希望实例化一个 UnitOfWork,该 UnitOfWork 还希望通过 TypeFactory 实例化一个类型。这是在不同的线程上。 TypeFactory 仍处于锁定状态,因为您仍在构造 MainViewModel。
再次,Catel 在 ViewModelBase 上提供了 Initialize 方法,该方法在创建的外部 中调用,因此可以安全地在其中执行任何操作。请改用它。
【讨论】:
DbContext 创建工作,例如var a = new TSDbContext(); a.处置();返回 5; //工作!但是 UnitOfWork => DeadLock Last Line 输出窗口 [DEBUG] [Catel.IoC.ServiceLocator] 注册类型 'Catel.Data.IConnectionStringManager' 以键入 'Catel.Data.ConnectionStringManager' 我尝试了 Initialize 方法(在 View.Loaded 事件中调用)和命令,都可以使用以下代码正常工作: public async Task我想我知道这里可能是什么问题。如果我对问题的理解是正确的,下面的代码会重现它:
public partial class MainWindow : Window
class Model
Model()
public Task<int> AsyncTask get; private set;
public static Model Create()
var model = new Model();
Func<Task<int>> doTaskAsync = async () =>
await Task.Delay(1);
return 42;
;
model.AsyncTask = doTaskAsync();
return model;
public MainWindow()
InitializeComponent();
private void Button_Click(object sender, RoutedEventArgs e)
var textBlock = new TextBlock();
var window = new Window Content = textBlock ;
window.Loaded += (sIgnore, eIgnore) =>
// int result = ((Model)window.DataContext).AsyncTask.Result;
// textBlock.Text = result.ToString();
;
window.DataContext = Model.Create();
window.ShowDialog();
MessageBox.Show("Result: " +
((Model)window.DataContext).AsyncTask.Result);
取消注释注释行,window.Loaded
事件处理程序内部将出现死锁,((Model)window.DataContext).AsyncTask.Result
。
这是因为window.Loaded
在调用ShowDialog
的Dispatcher 消息循环的同一迭代中同步触发。 AsyncTask
没有机会完成,因为 await Task.Delay(1)
之后的继续被安排在 UI 线程的 DispatcherSynchronizationContext
上。
相同的AsyncTask.Result
代码在ShowDialog
之后立即起作用。这是因为在关闭对话框之前,已经执行了更多消息循环的迭代(在对话框的新 Dispatcher 框架上)。
修复很简单:
window.Loaded += async (sIgnore, eIgnore) =>
int result = await ((Model)window.DataContext).AsyncTask;
textBlock.Text = result.ToString();
;
这将在对话框的 Dispatcher 框架上异步完成任务。
我不确定这与 OP 的情况有多接近,因为在上述情况下放置 await Task.Delay(1).ConfigureAwait(false)
也可以解决问题。不过,这是我根据 OP 的代码所能做出的猜测。
【讨论】:
问题是他说他是在 Command 中这样做的。我尝试使用 DbContext 和 UnitOfWork / Repository 重现此问题,但无法将其挂起(在 Initialize (View.Loaded) 和命令中)。 @GeertvanHorrik,该命令只是另一种事件。如果它是同步触发的,在ShowDialog
进入其模态消息循环之前的某个地方,效果将是相同的。
据我了解,他显示了 Login 窗口,并在 Login 窗口中执行了将调用 的命令Login 调用(这是异步的)。但他应该只是想出一个很好的可重现案例。
@GeertvanHorrik,我同意。到目前为止,我们只能做出猜测。
仅供参考:我们发现了问题。 TypeFactory 上存在多线程死锁。查看其他答案。以上是关于ShowDialog 中的 Catel async await 命令 - 死锁的主要内容,如果未能解决你的问题,请参考以下文章
Flutter showDialog 如何刷新 setStatus