你好想象我有这样的代码:
0. void someFunction()
1. {
2. ...
3. if(x>5)
4. doSmth();
5.
6. writeDataToCard(handle, data1);
7.
8. writeDataToCard(handle, data2);
9.
10. incrementDataOnCard(handle, data);
11. }
事情如下。如果步骤6& 8执行,然后有人说删除卡 - 然后操作10将无法成功完成。但这将是我系统中的一个错误。意思是6&执行8,然后必须执行10。如何处理这种情况?
快速摘要:我的意思是说在第8步之后有人可能会删除我的实体卡,这意味着永远不会达到第10步,这将导致我的系统出现问题。即卡片将被初始化为不完整的数据。
答案 0 :(得分:2)
你必须创建某种protcol,例如你在卡片上写下要完成的操作列表:
当您完成任务时,您将从列表中删除该条目。
当您重新读取磁盘中的数据时,如果仍有任何条目,则检查列表。如果是,则操作在之前未成功完成并恢复之前的状态。
除非您能以某种方式在物理上阻止用户移除卡,否则没有别的办法。
答案 1 :(得分:2)
如果交易中断,则卡处于故障状态。您有三种选择:
在这三种情况下,您需要在卡片上标明正在进行的交易。
答案 2 :(得分:0)
要回答此问题,需要更多详细信息。
但是,做一些假设,我会建议两种可能的解决方案(更多可能......)。 我假设写操作是持久的 - 因此在卡被移除 - 重新插入后,写入卡的数据仍然存在,并且您指的是卡上数据的一致性 - 而不是执行函数调用的程序的状态。 还假设增量方法增加已写入的数据,并且系统必须完成此操作以保证一致性:
对于每个写入的记录,维护另一个表示记录状态的数据元素(在卡上)。在执行writeData操作之前,此状态将初始化为某些内容(例如“WRITING”状态)。然后,在incrementData操作(成功!)执行后,将此状态设置为“WRITTEN”。 从卡片中读取时 - 首先检查此状态,如果记录不是书面记录,则忽略(或删除)记录。
另一种选择是在卡上维护两个(持久性)计数器:一个计算开始写入的记录数,另一个计算结束写入的记录数。 在执行写入之前递增第一个,然后在(成功)执行incrementData调用之后递增第二个。 稍后从卡上读取时,您可以轻松检查记录是否确实有效,或者是否需要丢弃。 如果书面记录以某种方式排序或编入索引,则此选项有效,因此您只需通过检查计数器即可查看哪些记录和有效记录有效。它的优点是只需要两个计数器用于任意数量的记录(与选项1中的EACH记录的1个状态相比。)
在主机(软件)端,您需要在开始写入之前检查该卡是否可用(如果不存在,则不要写入)。如果在incrementData操作之后您发现卡已被删除,则需要确保在检测到卡被重新插入后或在执行另一次写入之前整理(删除未完成的记录,更新计数器)。为此,您需要在软件方面维护状态信息。
同样,解决方案的类型(更多)取决于确切的系统和要求。
答案 3 :(得分:0)
不只是:
在两个重命名步骤中,您仍然会遇到竞争条件(如果幸运用户删除了卡),但您可以恢复数据或temporary_data。
答案 4 :(得分:0)
你还没有说过你正在增加什么(或为什么),或者你的数据是如何构建的(可能是你用writeDataToCard
写的任何内容和你正在增加的内容之间都存在某种关系。
因此,虽然可能有一些特定于您的数据的聪明技术,但我们还没有足够的技术继续下去。以下是明显的通用技术:
可能最简单的事情 - 全卡提交或回滚
保留所有数据的两个副本, good 一个, dirty 一个。最低地址的单个字节足以说明哪个是当前好的(它本质上是一个大小为2的数组的索引)。
将新数据写入脏区,完成后,更新索引字节(以便交换干净和脏)。
索引已更新且您的新数据全部正常,或者卡已拉出,之前的干净副本仍处于活动状态。
专业 - 非常简单
Con - 您浪费的存储空间只有一半,当您更改任何内容时,您需要将完整的新副本写入脏区/ em>的。您没有提供足够的信息来判断这是否对您有用。
...现在使用更少的空间...... - 提交或回滚更小的子集
如果您不能浪费50%的存储空间,请将数据拆分为独立的块,然后单独对每个那些进行版本化。现在你只需要足够的空间来复制你最大的单个块,但是你需要一个简单的索引来代替每个块的偏移量或指针。
专业 - 仍然相当简单
Con - 你无法处理块之间的依赖关系,它们必须被隔离
<强>轴颈强>
根据RedX的回答,许多文件系统都使用它来保持完整性。
Pro - 这是一项可靠的技术,您可以找到现有文件系统的文档和参考实现
Con - 您刚刚编写了一个现代文件系统。这真的是你想要的吗?