网志

重要的编程概念(甚至在嵌入式系统上)第一部分:幂等

杰森·萨克斯(Jason Sachs)2014年8月26日5条评论

实际上,有数百个(即使不是数千个)细微的概念也可以构成高质量的软件设计。其中许多是众所周知的,可以在书籍或Internet中找到。一世’我将重点介绍一些我认为重要且经常被忽略的内容。

但首先让’s从短暂的转移开始。一世’我要大胆地声明:除非你’re a novice, there’s 至少一件事 在计算机编程中,关于您’我养成了一个坏习惯。

无论你’一旦您编程了4个月或40年,’从学习编程的初期阶段开始,现在您已经对自己的能力充满信心,这是一把双刃剑。一方面,您可以停止专注于每件事,而专注于手头的任务。另一方面,这意味着您的某些工作是在自动驾驶仪上进行的。人的头脑在受到纪律方面很糟糕。它喜欢快捷方式。它’懒。当计算机看到不正确的东西时会发出嘎嘎叫声,为什么还要花些力气才能做到完美?让计算机进行肮脏的工作。尽管计算机毫不留情地拒绝最小的语法错误,但它接受任何超出其基本标准的东西。在较低层次上,静态分析工具例如 皮棉查找错误 will help catch more nuanced mistakes that get programmers into trouble. (Classic example in C: if (age = 13) { printf("Congrats! 您're a teenager!\n"); } —您能发现错误吗?)这些工具赢得了’但是,它不能帮助您解决高级软件设计错误。

本文以PDF格式提供,便于打印

所以那里’真的无法替代学习— 和 re-learning — 通过编程实践来淘汰不良行为,并帮助您保持领先地位。像书 代码完成, 实用程序员, 有效的C ++有效的Java 是指出最佳编程技术的最佳方法。并寻找同事学习!书是使您了解良好做法的好方法,但是在那里’不能代替有一个可以查看您的代码并为您提供建设性反馈的人。

本系列的其他文章:

现在进入我们的特色演讲。

幂等

好吧,我们需要坐下来认真讨论幂等。

不,我说 幂等!它’一个花哨的词,仅仅意味着那里’连续执行一次,两次或五次操作没有区别。

想象你是一个心不在professor的教授。您的记忆力很差。实际上,如果您要确保完成任务,则必须将它们写下来。即使这样,有时您也会忘记。这是您今天的事情清单:

  • A组
    1. 出售100股Glyptocratic Incorporated(GLYP)的股票
    2. 等待您的一个学生打电话给我,并花20分钟帮助他们完成本周的工作’功课让其他人受苦
  • B组
    1. 那里 should be \$1180 left in bank account; if there is, withdraw \$60
    2. 车漆成红色
    3. 去除汽车挡风玻璃上的贴纸

您’今天必须完成这些事情。它’重要。每当查看该列表时,您都会忘记是否做了其中的一些操作。所以如果你’不确定,只是假设他们需要完成。

最后,这里’s what happened:

  • 您 sold 300 shares of GLYP
  • 您 spent an hour helping 3 students with their homework
  • 那里’s \ $ 1120在您的银行帐户中—第一次去银行时,您取出了\ $ 60,第二次去银行时,您感到有些无法满足
  • 您r car is red, 和 you have two bills from Ed’s Body Shop (and you’不确定昨天的车是否也红色)
  • 您的汽车挡风玻璃上没有贴纸— the ones that were there are now in the trash 后 an hour of grueling work in the garage, 和 the three additional times you went out to your car, you were so glad to see that you didn’t have to do anything 后 all.

好那’是一个人为的例子。但我希望你明白这一点。 B组中的任务是幂等的:’不管您执行一次,两次还是几次,最终结果都是相同的。 (嗯,基本上是一样的。太可惜了,您必须支付两次才能使新涂的红色汽车再次涂成红色。)A组中的任务不是。

In programming, this sort of thing happens with distributed systems. And 之前 you say that distributed systems programming is one of those fancy things you don’无需处理,无论何时使用网络插座,串行端口或SPI / I2C芯片— anywhere it’不能100%保证您的数据正确到达,’是不可靠的通讯渠道,因此您必须准备重试任何通讯事务。

关于如何不处理不可靠通信的经典坏例子:您的互联网商务网页’大家都看过,说“不要多次单击“提交””并承诺如果您这样做会带来可怕的后果,例如错误地订购了两个Ronco Salad Shooters,因为您的手指滑了鼠标按钮。它’一个不好的例子,因为它 ’如此容易修复。非幂等方法是每次客户单击按钮时都会触发新的采购订单。网络浏览器向服务器发送请求(“适用于James Q MCGILLICUDDY的ORDER 1 RONCO SALAD SHOT”),并且每次执行此操作时,服务器都会通过启动另一个Ronco Salad Shooter的订单来兑现并采取行动。单击两次,订购两次。

那里 are 好 和 bad solutions to this problem.

糟糕的解决方案包括尝试以某种方式将网页设计为 愚蠢的客户问题,应该为客户提供视觉提示,使他们不太可能双击“提交”按钮,或者商家必须主动寻找客户的重复交易’代表。另一个那个’问题较少,但仍然不是很大,是 disable the SUBMIT button 后 the first click 这样客户可以’单击两次“提交”,这只是一个简单的解决方案…除非客户暂时遇到Internet连接问题,必须重新加载网页并且没有’不知道是否已下订单,因此他单击“提交”,结果得到了两个Ronco Salad Shooters。

正确的解决方案是设计一个幂等的通信协议,并避免依赖客户或Web浏览器而不会出错。我们’ll 为交易分配唯一的ID 在订购过程的早期。当客户希望完成订单时,我们使用带有交易ID的请求,例如“TRANSACTION 123456 适用于James Q MCGILLICUDDY的ORDER 1 RONCO SALAD SHOT”,并设计服务器,使其永远不会多次执行给定的事务。它没有’客户提交订单一次还是17次无关紧要。成功执行的第一个将被执行,随后的订单将被忽略,或者可能会返回一条消息,提示客户“We’我们已经收到您的订单。”

如果您认为这只是HTTP客户端-服务器应用程序的问题,请再考虑一遍。幂等甚至在非常简单的嵌入式系统(例如微波炉或车库门开启器)中也会显示。键和按钮已经发展,因此它们’现在重新使用电容式触摸传感器—从正面看,没有机械零件磨损,但是从正面看,没有机械反馈让您知道您’已成功按下按钮。这使得很难分辨你是否’按下一次或两次或根本不按下按钮。尽管车库门开启器似乎仍使用带有触觉反馈的老式快动按钮,但从本质上讲,它们仍是通信设备。有时,我必须按我的两到三遍,然后车库中的传感器将其捡起并打开门。有时,如果我不止一次按下它,我会错过门打开的感觉,而下一次按下会使车库门反向并关闭。啊!

再来这里’至少在理论上是一个简单的解决方案。将按钮替换为拨动开关。

拨动拨动开关时,而不是发送到车库门机构的拨动消息— meaning “如果关闭则为开门,如果关闭则为开门” —如果拨动开关打开,则发送至车库门机构的消息为“OPEN THE GARAGE DOOR”,如果拨动开关已按下,则发送至车库门机构的消息为“关闭车库门”。当然,这需要车库门机构知道门是向上还是向下(’m not sure there’传感器),并且需要更改发射器的硬件(在车库门开启器中使用拨动开关似乎很奇怪)和软件(遥控器必须在5秒钟后发送重复消息5秒钟)开关已翻转状态)。如果您有多个发射器,’d必须弄清楚如何对待相互矛盾的输入… so it’可能不太实用。但是拨动开关的系统含义至少值得考虑。

The next time you have to design a device with inputs, think carefully 之前 you add a push button. Knobs 和 toggle switches are very effective devices that don’幂等有问题。您可以’t错误地将电灯开关打开两次,或错误地将恒温器拨盘两次打开到同一位置。

I’前面提到了两种幂等的解决方案:一种是发送带有事务ID的消息,另一种是发送带有目标状态的消息,该消息描述了所需的最终结果。第三种幂等技术可用于并发编程,它可以通过以下方式发送消息:“before” 和 an “after” state: include the assumed state 之前 the operation in question, in addition to the desired end result. This approach is used in the 比较和交换 用于实现信号量的操作。

这三种技术以及较小的变体始终出现在良好的编程实践中。作为回顾:

  • 非等幂运算:

    • 增加一个变量
    • 切换状态
    • 前进到下一个状态
  • 幂等运算:

    • 如果当前状态与X匹配,则将其更改为Y
    • 将变量设置为Y
    • 如果尚未执行具有唯一ID X的事务,则执行该事务

最后,不要’别忘了尽可能地利用幂等性。最常见的用途是执行冗余写入以提高系统可靠性。这里’我几年前遇到的一个例子:

我们有一个带有微控制器和SPI DAC的电路板。相当容易,只需将一些位时钟输出,DAC就会更新其模拟输出。问题在于,DAC没有提供串行输出引脚(MOSI),因此除了将DAC信号馈送到备用ADC通道外,没有其他方法可以读取任何数据,我们没有选择的余地。

为什么这是个问题?因为即使DAC和微控制器彼此紧靠,也无法保证通过SPI进行通信,所以该DAC的SPI协议没有提供错误信息。’没有任何校验和。校验和以及读取事务内容的能力是增强整体系统可靠性的方法。在我们的情况下,可能有4”芯片之间的距离,并且附近有开关电源电子设备。所以让’s say there’s a 10-9 每个DAC写入的可能性是DAC接收到的值的高3位与微控制器发送的值的高3位不同。这意味着大约每十亿次DAC写入,您将获得一个不好的DAC值,该值会起作用。不良DAC值的持续时间和影响将取决于写入频率。

如果您每小时写一次DAC,那’故障率非常小,在任何给定年份中,114,080的概率是一个’会看到不良的DAC值。但是它会出现一个小时,这在嵌入式系统中是相当长的时间。

如果你’每秒写一次DAC,在给定年份中,数字变成31.7的几率’会看到错误,但会持续一秒钟。

如果你’每毫秒重写一次DAC,它’现在很可能在一年的时间里’会看到一个错误(两次错误之间的平均时间是11.57天),但是只会持续一毫秒。

在我们的案例中,系统设计仅要求我们在上电时设置DAC。但是这种很小的失败风险会持续几天,因此我们决定每隔几毫秒定期编写一次DAC。我宁愿使用具有回读功能的DAC,但在设计电路板时却忽略了这一点,因此,我们使用幂等来减轻整个系统出错的风险。

今天结束了’s topic.

下一个概念:不变性…


©2014 Jason M. Sachs,保留所有权利。


杰森·萨克斯(Jason Sachs)上一篇文章:
   有一天我们会找到它,开尔文联系
杰森·萨克斯(Jason Sachs)下一篇文章:
   重要的编程概念(甚至在嵌入式系统上)第二部分:不变性

[-]
评论者 斯科特402014年8月26日
我要提醒不要过度分析幂等...或分析不足。
我见过人们将幂等性定义为简单的意思-如果多次调用将得出相同的结果。如果要检索数据,这是一个很好的定义:“给我我当前的余额”服务本质上是幂等的。但是,如果您的“将我的车漆成红色”采用了这种方法,那将是错误的。第一次遇到一辆红色轿车。第二次您打电话给商店时,他们说“好,您的车是红色的”,并且优雅地决定不向您收费。他们没有做的就是看你的车,发现某个小丑在半夜破坏了它,并在车上涂了粉红色的兔子。

当您做更复杂的事情时,这是不够的。幂等性实际上意味着“要做使宇宙处于特定状态的必要操作。要使“将我的车漆成红色”是幂等的,它必须考虑您的车(其宇宙)的当前状态并弄清楚需要做什么。将其转换为红色,除兔子绘画破坏者以外,仍将是红色。

另一方面,对幂等性的分析涉及将并非真正存在于宇宙中的物体视为相关。同样,在您的示例中,读取我的汽车的油漆实际上仅与汽车的颜色有关,而与停车位置无关。再次以您的示例为例,星期一您将汽车漆成红色,然后开车回家。您将车停在了2个车库的左侧。星期三,您又去了商店,把车漆成红色。但是,由于您与妻子做了晚餐约定,因此决定将她的便条放在冰箱上,这样她就不会担心您是否迟到:“去了13:53将汽车漆成红色。预计完成时间为17:45 ”。您去商店时,他们看着您很有趣,并提醒您您的车已经变红并送您回家。您将车停在车库的右侧。

有些人会试图告诉您这不是幂等的,要么是因为您在冰箱上留下了日志消息,要么是因为您没有将汽车放在同一地点。试图在“宇宙”状态下包含无关紧要的条件是另一种常见的谬误,那就是您的汽车是红色的,如果您在冰箱上留下粘滞便笺或将其存放在冰箱里,它会给老鼠屁股。操作完成后的另一个位置。
[-]
评论者 山姆·斯库斯2014年8月26日
我认为您在“问题在于DAC没有提供串行输出引脚(MOSI)”中有错字-我认为您的意思是MISO,主从输出,除非您的DAC是SPI主设备。好文章!
[-]
评论者 jms_nh2014年8月26日
已修复,感谢您指出!
[-]
评论者 斯科特402014年8月26日
嘘我爱错别字8-)
[-]
评论者 木板打屁股2019年7月3日

不错的文章。我怀疑使用推(轻触)开关而不是拨动开关的原因之一是成本因素。拨动开关更昂贵。另一个原因是外形尺寸,拨动开关从设备中伸出,而触觉开关与外壳齐平或接近齐平。

当然是一个令人发指的讨论。就个人而言,我宁愿出于您所建议的原因使用切换开关。我们只需要说服bean计数器不要降低我们的设计成本,而让消费者为更好的产品付出更多。在当前的可支配经济中很难实现!

要发布对评论的回复,请单击每个评论所附的“回复”按钮。要发布新评论(而不是回复评论),请查看评论顶部的“写评论”标签。

注册后,您可以参加所有相关网站上的论坛,并获得所有pdf下载的访问权限。

注册

我同意 使用条款隐私政策.

试试我们偶尔但很受欢迎的时事通讯。非常容易退订。
或登录