gpt4 book ai didi

c# - ManualResetEvent WaitOne 阻止我的 CollectionView 的所有者线程

转载 作者:太空狗 更新时间:2023-10-29 21:50:22 27 4
gpt4 key购买 nike

我编写了一个 WPF WizardFramework,它使用一些 BackgroundWorker 在后台执行一些操作。在处理过程中,我可能必须更新绑定(bind)到我的 UI 的 ObservableCollection

对于这种情况,我编写了一个ThreadableObservableCollection,它为InsertRemoveRemoveAt 提供了线程安全的方法>。尽管我使用的是 .NET 4.5,但在没有许多其他无效访问异常的情况下,我无法使 BindingOperations.EnableCollectionSynchronization 正常工作。我的 Collection 看起来像:

  public class ThreadableObservableCollection<T> : ObservableCollection<T>
{
private readonly Dispatcher _dispatcher;
public ThreadableObservableCollection()
{
_dispatcher = Dispatcher.CurrentDispatcher;
}

public void ThreadsafeInsert(int pos, T item, Action callback)
{
if (_dispatcher.CheckAccess())
{
Insert(pos, item);
callback();
}
else
{
_dispatcher.Invoke(() =>
{
Insert(pos, item);
callback();
});
}
}

[..]
}

当我在我的应用程序中使用该向导时,这按预期工作。现在我正在使用 NUnit 为应用程序编写一些集成测试。

有一个监听器等待 WizardViewModel 完成它的工作并寻找一些注入(inject)到步骤集合中的页面。 asyncrone 工作完成后,我可以使用 Validate 检查 viewmodel 状态。

不幸的是,我正在使用 ManualResetEvent 来等待向导关闭。这看起来像下面这样:

  public class WizardValidator : IValidator, IDisposable
{
private WizardViewModel _dialog;
private readonly ManualResetEvent _dialogClosed = new ManualResetEvent(false);

[..]

public void ListenTo(WizardViewModel dialog)
{
_dialog = dialog;
dialog.RequestClose += (sender, args) => _dialogClosed.Set();
dialog.StepsDefaultView.CurrentChanged += StepsDefaultViewOnCurrentChanged;

_dialogClosed.WaitOne();
}

[..]
}

现在有一个问题:当应用程序运行时,UI 线程不会被阻塞,可以毫无问题地更新集合。但是在我的测试用例中,我初始化 ViewModel 的“主”线程(因此是集合)是一个被测试代码阻止的 AppDomainThread。现在我的 ThreadsafeInsert 想要更新集合但不能使用 AppDomain 线程。

但是我必须等待向导完成,我该如何解决这种死锁?还是有更优雅的解决方案?

编辑:我通过检查是否有用户界面解决了这个问题,然后才在应用程序线程上调用,否则我有意在另一个线程上更改集合。这不会阻止异常,但无法从测试中识别出来...尽管如此,项目仍被插入,只有 NotifyCollectionChanged-Handler 未被调用(无论如何仅在 UI 中使用)。

  if (Application.Current != null)
{
Application.Current.Dispatcher.Invoke(() =>
{
Steps.Insert(pos, step);
stepsView.MoveCurrentTo(step);
});
}
else
{
new Action(() => Steps.Insert(pos, step)).BeginInvoke(ar => stepsView.MoveCurrentToPosition(pos), null);
}

这是一个丑陋的解决方法,我仍然对干净的解决方案感兴趣。

有没有办法使用备用 Dispatcher 来创建(例如)整个 ViewModel 并使用它来更改我的集合?

最佳答案

如我所见,主要问题是主线程被阻塞,其他操作也试图在主线程中执行?不阻塞主线程怎么样,就像这样:

// helper functions
public void DoEvents()
{
DispatcherFrame frame = new DispatcherFrame();
Dispatcher.CurrentDispatcher.BeginInvoke(DispatcherPriority.Background,
new DispatcherOperationCallback(ExitFrame), frame);
Dispatcher.PushFrame(frame);
}

public object ExitFrame(object f)
{
((DispatcherFrame)f).Continue = false;

return null;
}

// in your code:
while(!_dialogClosed.WaitOne(200))
DoEvents();

如果它没有帮助,那么我想需要尝试一些 SynchronisationContext 解决方法。

关于c# - ManualResetEvent WaitOne 阻止我的 CollectionView 的所有者线程,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20634057/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com