如何处理原子性的情况

时间:2013-09-30 14:46:21

标签: c++

你好想象我有这样的代码:

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步,这将导致我的系统出现问题。即卡片将被初始化为不完整的数据。

5 个答案:

答案 0 :(得分:2)

你必须创建某种protcol,例如你在卡片上写下要完成的操作列表:

  1. 步骤6,步骤8,步骤10
  2. 当您完成任务时,您将从列表中删除该条目。

    当您重新读取磁盘中的数据时,如果仍有任何条目,则检查列表。如果是,则操作在之前未成功完成并恢复之前的状态。

    除非您能以某种方式在物理上阻止用户移除卡,否则没有别的办法。

答案 1 :(得分:2)

如果交易中断,则卡处于故障状态。您有三种选择:

  1. 什么都不做。卡处于故障状态,它将保持在那里。建议用户不要使用该卡。卡可以有资格完全清理或格式化。
  2. 下次卡可用时回滚交易。您需要有关卡和/或某些中央存储库的足够信息才能执行回滚。
  3. 下次卡可用时完成交易。您需要有关卡和/或某个中央存储库的足够信息才能执行完成。
  4. 在这三种情况下,您需要在卡片上标明正在进行的交易。

答案 2 :(得分:0)

要回答此问题,需要更多详细信息。

但是,做一些假设,我会建议两种可能的解决方案(更多可能......)。 我假设写操作是持久的 - 因此在卡被移除 - 重新插入后,写入卡的数据仍然存在,并且您指的是卡上数据的一致性 - 而不是执行函数调用的程序的状态。 还假设增量方法增加已写入的数据,并且系统必须完成此操作以保证一致性:

  1. 对于每个写入的记录,维护另一个表示记录状态的数据元素(在卡上)。在执行writeData操作之前,此状态将初始化为某些内容(例如“WRITING”状态)。然后,在incrementData操作(成功!)执行后,将此状态设置为“WRITTEN”。 从卡片中读取时 - 首先检查此状态,如果记录不是书面记录,则忽略(或删除)记录。

  2. 另一种选择是在卡上维护两个(持久性)计数器:一个计算开始写入的记录数,另一个计算结束写入的记录数。 在执行写入之前递增第一个,然后在(成功)执行incrementData调用之后递增第二个。 稍后从卡上读取时,您可以轻松检查记录是否确实有效,或者是否需要丢弃。 如果书面记录以某种方式排序或编入索引,则此选项有效,因此您只需通过检查计数器即可查看哪些记录和有效记录有效。它的优点是只需要两个计数器用于任意数量的记录(与选项1中的EACH记录的1个状态相比。)

  3. 在主机(软件)端,您需要在开始写入之前检查该卡是否可用(如果不存在,则不要写入)。如果在incrementData操作之后您发现卡已被删除,则需要确保在检测到卡被重新插入后或在执行另一次写入之前整理(删除未完成的记录,更新计数器)。为此,您需要在软件方面维护状态信息。

    同样,解决方案的类型(更多)取决于确切的系统和要求。

答案 3 :(得分:0)

不只是:

  • 将数据复制到temporary_data。
  • 写入temporary_data。
  • 增加temporary_data。
  • 将数据重命名为old_data。
  • 将temporary_data重命名为数据。
  • 删除old_data。

在两个重命名步骤中,您仍然会遇到竞争条件(如果幸运用户删除了卡),但您可以恢复数据或temporary_data。

答案 4 :(得分:0)

你还没有说过你正在增加什么(或为什么),或者你的数据是如何构建的(可能是你用writeDataToCard写的任何内容和你正在增加的内容之间都存在某种关系。

因此,虽然可能有一些特定于您的数据的聪明技术,但我们还没有足够的技术继续下去。以下是明显的通用技术:

  1. 可能最简单的事情 - 全卡提交或回滚

    保留所有数据的两个副本, good 一个, dirty 一个。最低地址的单个字节足以说明哪个是当前好的(它本质上是一个大小为2的数组的索引)。

    将新数据写入脏区,完成后,更新索引字节(以便交换干净和脏)。

    索引已更新且您的新数据全部正常,或者卡已拉出,之前的干净副本仍处于活动状态。

    专业 - 非常简单

    Con - 您浪费的存储空间只有一半,当您更改任何内容时,您需要将完整的新副本写入脏区/ em>的。您没有提供足够的信息来判断这是否对您有用。

  2. ...现在使用更少的空间...... - 提交或回滚更小的子集

    如果您不能浪费50%的存储空间,请将数据拆分为独立的块,然后单独对每个那些进行版本化。现在你只需要足够的空间来复制你最大的单个块,但是你需要一个简单的索引来代替每个块的偏移量或指针。

    专业 - 仍然相当简单

    Con - 你无法处理块之间的依赖关系,它们必须被隔离

  3. <强>轴颈

    根据RedX的回答,许多文件系统都使用它来保持完整性。

    Pro - 这是一项可靠的技术,您可以找到现有文件系统的文档和参考实现

    Con - 您刚刚编写了一个现代文件系统。这真的是你想要的吗?