1

我的应用程序中有一个PropertyGrid用于编辑任意对象。我需要能够在另一个查看这些对象的线程上运行任意子例程(搜索功能,如果你好奇的话)。显而易见的问题是,用户可能在我的搜索线程正在读取其中一个对象的同时正在编辑它,最好避免这种情况(尽管它可能不会导致任何关键问题,因为我的搜索线程只是在读取,不写)。

从我的搜索线程中调用lock(obj)很容易,但是在浏览了文档并简要浏览了 Reflector 中的 PropertyDescriptorGridEntry 代码后,我似乎找不到类似的位置来System.Threading.Monitor.Enter()/Exit()调用 PropertyGrid 上的相关对象。我希望有 BeginEdit 和 EndEdit 事件可以使这变得足够简单,但我似乎找不到任何这样的东西。我宁愿在 PropertyGrid 中显示时不锁定整个对象,因为这显然会阻塞我的搜索线程,直到选择另一个对象。

我对 Windows 窗体的线程模型有点陌生,所以我希望有一些我刚刚忽略的明显答案。有什么帮助吗?

编辑:在异步运行搜索之前同步克隆我的对象可能效率很低,以至于我还不如同步运行搜索本身 - 异步运行的目的当然是让我的用户在搜索执行时继续工作。搜索需要很好地扩展,因为我正在经历的数据集最终会变得任意大,这使得同步克隆看起来会导致我试图避免的可用性问题。

4

4 回答 4

0

那确实不是线程安全的。您可以将搜索代码的一部分编组到 UI 线程,但这会减慢速度,并且可能会破坏线程的要点......

搜索需要多快?你能对付克隆人吗?ETC

于 2009-04-01T19:37:22.033 回答
0

您可以在显示数据之前克隆数据,并让您的搜索线程在克隆上工作吗?如果您希望搜索线程“看到”更改,您可以响应事件,PropertyGrid并可能以某种方式对克隆进行受控更改。(不过,使用“陈旧”数据可能更容易。)

我知道克隆数据听起来非常低效,但是当每个线程处理完全独立的数据(或所有线程只读取)时,线程肯定更简单。

于 2009-04-01T19:37:43.280 回答
0

我认为您将不得不为此做很多工作。

首先,您必须创建 ICustomTypeDescriptor 的实现,该实现将基于您通常为该类型获得的任何 TypeDescriptor。

然后,在公开要锁定的成员的描述符的方法的实现中,您将提供从这些描述符派生的子类并覆盖适当的方法以包裹锁定。

因此,对于一个属性,您将实现 GetProperties 以返回您的 PropertyDescriptor 的特定子类。这些子类将覆盖 GetValue 和 SetValue 方法(以及其他方法)并在访问这些变量时使用锁。

应该提到的是,在 UI 线程中像这样锁定可能是一个坏主意,因为您不想随意阻止该线程上的操作。最好只创建对象的克隆,然后有一个方法在完成后更新对象的存储。

于 2009-04-01T19:40:29.030 回答
0

我可能是错的,但在我看来,你来自错误的一端。

您需要解决两个不同的问题。

首先,确保仅在 UI 线程上访问 PropertyGrid。如果从其他线程访问它的任何方法(包括属性 getter/setter),您将以神秘的方式遭受痛苦。当然,例外是InvokeRequired()Invoke

其次,确保您的搜索线程可以正常运行。

要解决第一个问题,要么确保你的对象除了UI 线程之外永远不会被修改,或者让你的所有事件触发器线程都知道,以便你的对象事件(例如 PropertyChanged)只在 UI 线程上触发。

第二个问题更容易——只要你的搜索线程只从你的核心对象中读取,一切都应该正常工作。是的,您的搜索线程可能会无意中看到一些部分更新的数据,但这真的是个问题吗?

最后的一些想法......

  • 不要实现您的搜索来遍历 PropertyGrid 本身;预先获取对象列表(它们不需要是克隆)并通过它来代替。

  • 您是否考虑过使用空闲处理进行搜索?每当应用程序用完要处理的消息时,该Application对象就会触发一个事件 - 您可以挂钩并在每次触发事件时执行搜索的 1 步。一种穷人线程,但没有互斥锁/锁/信号机头痛。我过去用这个效果很好。

更新:如果我没记错的话,PropertyGrid 尊重IEditableObject接口,BeginEdit在您开始修改一行以及EndEdit移动到另一行时立即调用。您可以利用它来防止您的搜索线程看到不完整的更改。

更新 2:跟进,我发现了你已经知道的 - PropertyGrid尊重 IEditableObject 接口。

我确实有另一个建议——尽管它可能比你想投资的要多。

大约在 .NET 2.0 中引入 System.Transactions 命名空间时,我看到了一篇关于使用环境事务为对象提供线程隔离的文章。这个想法是您的对象属性具有双重存储。首先,您拥有对所有线程可见的已提交值,并且您拥有一个线程局部变量,用于在每个线程的基础上存储未提交的值。修改属性时,对象通过将新值存储在本地线程中来加入任何环境事务。当事务提交或回滚时,线程的值要么被存储要么被丢弃。

不幸的是,我找不到原始文章,尽管 CSLA 似乎提供了这种支持。希望这可以帮助。

于 2009-04-01T23:41:40.120 回答