【问题标题】:Serialization of objects: no thread state can be involved, right?对象的序列化:不能涉及线程状态,对吧?
【发布时间】:2010-09-16 02:09:06
【问题描述】:

我正在努力研究将执行程序的状态存储到磁盘并再次将其恢复的基本原则。在我们目前的设计中,每个对象(这是一个带有函数指针列表的 C 级事物,一种低级的自制面向对象——这样做有很好的理由)将是调用以将其显式状态导出为可写和可恢复的格式。使这项工作的关键属性是与对象相关的所有状态确实封装在对象数据结构中。

还有其他解决方案可以处理活动对象,其中有一个用户级线程附加到某些对象。因此,程序计数器、寄存器内容和堆栈内容突然成为程序状态的一部分。据我所知,没有好的方法可以在任意时间点将这些东西序列化到磁盘上。线程必须将自己停放在程序计数器等不表示任何内容的特殊状态中,因此基本上将它们的执行状态机状态“保存”到显式对象状态。

我查看了一系列序列化库,据我所知,这是一个通用属性。

核心问题是:或者事实并非如此?是否有保存/恢复解决方案可以包括线程状态,就线程在其代码中执行的位置而言?

请注意,在虚拟机中保存整个系统状态不算数,这并不是真正序列化状态,而只是冻结机器并移动它。这是一个显而易见的解决方案,但大多数时候有点重量级。

有些问题清楚地表明,我在解释我们如何做事的想法时不够清楚。我们正在开发一个模拟器系统,对于在其中运行的代码有非常严格的规则是允许编写的。特别是,我们完全区分了对象构造和对象状态。每次设置系统时都会重新创建接口函数指针,而不是状态的一部分。状态仅由特定指定的“属性”组成,每个“属性”都具有定义的获取/设置函数,该函数在内部运行时表示和存储表示之间进行转换。对于对象之间的指针,它们都被转换为名称。所以在我们的设计中,一个对象可能会像这样在存储中出现:

Object foo {
  value1: 0xff00ff00;
  value2: 0x00ffeedd;
  next_guy_in_chain: bar;
}

Object bar {
  next_guy_in_chain: null;
}

链表在模拟结构中从未真正存在,每个对象代表某种硬件单元。

问题是有些人想这样做,但也有线程作为编码行为的一种方式。这里的“行为”实际上是模拟单元状态的突变。基本上,我们的设计说所有这些更改都必须在原子完整操作中进行,这些操作被调用、完成它们的工作并返回。所有状态都存储在对象中。你有一个反应模型,或者它可以被称为“运行到完成”或“事件驱动”。

另一种思考方式是让对象有活动线程在其上工作,这些线程以与经典 Unix 线程相同的方式处于一个永恒的循环中,并且永远不会终止。这是我试图查看是否可以合理地将其存储到磁盘的情况,但是如果不在下面插入 VM,这似乎是不可行的。

2009 年 10 月更新:与此相关的论文发表在 2009 年的 FDL 会议上,请参阅 this paper 关于检查点和 SystemC。

【问题讨论】:

    标签: java c++ multithreading serialization systemc


    【解决方案1】:

    我认为仅序列化程序的“某些线程”是行不通的,因为您会遇到同步问题(这里描述了一些问题http://java.sun.com/j2se/1.3/docs/guide/misc/threadPrimitiveDeprecation.html)。 因此,持久化整个程序是获得一致状态的唯一可行方法。

    您可能会研究正交持久性。有一些原型实现:

    http://research.sun.com/forest/COM.Sun.Labs.Forest.doc.external_www.PJava.main.html

    http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.17.7429

    但是它们都不再被维护或获得很多吸引力(afaik)。我想检查点毕竟不是最好的解决方案。在我自己的项目http://www.siebengeisslein.org 我正在尝试使用轻量级事务来调度事件的方法,因此不必维护线程状态(因为在事务结束时,线程调用堆栈再次为空,如果操作是在事务中间停止,一切都回滚,所以线程调用堆栈也很重要)。 您可能可以使用任何 OODBMS 实现类似的功能。

    另一种看待事物的方式是延续(http://en.wikipedia.org/wiki/Continuationhttp://jauvm.blogspot.com/)。它们是一种在定义的代码位置暂停执行的方法(但它们不一定保持线程状态)。

    我希望这能给你一些起点(但没有现成的解决方案来解决这个问题)。

    编辑:阅读您的说明后:您绝对应该研究 OODBMS。在自己的事务中调度每个事件,而不关心线程。

    【讨论】:

      【解决方案2】:

      这听起来真的像保存虚拟机的状态并能够以完全相同的方式恢复它正是您想要的。

      如果您只需要能够使用之前执行使用的相同数据来启动程序运行,那么您只需要保存和恢复持久数据,每个线程的确切状态应该并不重要,因为它无论如何都会变化得如此之快——而且下次事物的实际地址会有所不同。无论如何,使用数据库应该会给你这种能力。

      【讨论】:

      • 不完全是:您还必须能够将状态恢复到不同的实现中,例如,在不同类型的主机上。线程状态确实很重要,例如,如果它用于编码一些状态机,例如总线协议。
      【解决方案3】:

      比尝试序列化程序状态更好的方法是使用数据检查点实现Crash Only Software。您如何进行数据检查点将取决于您的实施和问题域。

      【讨论】:

        【解决方案4】:

        看起来您想要在 C++ 中使用 closure。正如您所指出的,语言中没有内置机制可以让您这样做。据我所知,这基本上不可能以完全通用的方式进行。一般来说,用没有 VM 的语言很难做到。你可以通过做一些你建议的事情来伪造它,基本上创建一个维护执行环境/状态的闭包对象。然后在它处于已知状态时对其进行序列化。

        您的函数指针也会遇到问题。函数可以在每次加载时加载到不同的内存地址。

        【讨论】:

          【解决方案5】:

          我认为线程状态是一个可能不适合序列化的实现细节。您想保存对象的状态——不一定要保存它们的状态。

          作为您为什么要采用这种方法的示例,请考虑无中断升级。如果您正在运行版本 N 的应用程序并想要升级到版本 N+1,则可以使用对象序列化来执行此操作。但是,“版本 N+1”线程与版本 N 线程没有什么不同。

          【讨论】:

            【解决方案6】:

            您不应该尝试序列化程序必须存储到磁盘的状态。因为您的程序永远无法完全控制其状态,除非操作系统允许,在这种情况下...它是操作系统的一部分。

            不能保证指向某个虚拟内存位置的指针会再次指向同一个虚拟内存位置(heap-begin/end、stack-begin 等属性除外),因为操作系统对虚拟内存的选择是不确定的。您通过 sbrk 或更高级别的接口(如 malloc)向操作系统请求的页面将在任何地方开始。

            更好:

            • 代码清理并检查您的设计:哪些状态属性是其中的一部分?
            • 不要使用这种低级语言,因为创建您尝试做的事情的开销不值得结果。
            • 如果您必须使用 C,请考虑使您的生活尽可能轻松的方法(考虑 offsetof 运算符和属性结构有类似第一个成员从偏移量 0 开始)。

            我怀疑您想缩短序列化/反序列化特定数据结构所需的开发时间,例如链表。请放心,您要尝试做的事情并非微不足道,而且需要做更多的工作。如果您坚持这样做,请考虑查看操作系统的内存管理代码和操作系统的分页机制。 ;-)

            EDIT 由于附加问题:您陈述的设计听起来像是某种状态机;对象属性设置为可序列化,函数指针可以恢复。

            首先,关于对象中的线程状态:只有在存在典型的并发编程问题(例如竞争条件)时才有意义。如果是这种情况,您需要线程同步功能,例如作为互斥体、信号量等。然后您可以随时访问属性以进行序列化/反序列化并确保安全。

            其次,关于对象设置:看起来很酷,不确定您是否有二进制或其他对象表示。假设二进制:如果您可以表示内存中的实际结构(这是一些编码开销),您可以轻松地序列化它们。 在对象的开头插入某种类 ID 值,并查找指向实际装备的查找表。查看第一个 sizeof(id) 字节,您就知道您拥有哪种结构。然后你就会知道那里铺设的是哪个结构。

            在序列化/反序列化时,请像这样处理问题:您可以查找假设打包(成员之间没有间距)结构的长度,分配该大小并一个接一个地读/写成员。想想 offsetof,或者,如果你的编译器支持它,就使用打包结构。

            由于大胆的核心问题而编辑 :-) 不,没有;不适合 C。

            【讨论】:

            • 嗯,这里的线程是 SystemC 中的东西,基本上是使用快速线程或 Windows 光纤的协作式非抢占式线程。在单个操作系统线程中。
            【解决方案7】:

            在 JSR 323 中实际上为 Java 提出了类似的建议:

            http://tech.puredanger.com/2008/01/09/strong-mobility-for-java/

            但由于过于理论化而未被接受:

            http://tech.puredanger.com/2008/01/24/jcp-votes-down-jsr-323/

            如果你点击链接,你可以找到一些关于这个问题的有趣研究。

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2016-05-11
              • 2012-02-29
              相关资源
              最近更新 更多