我正在与我的应用程序中的潜在竞争条件作斗争,它依赖于访问共享对象的多个线程来确定它们的下一个逻辑操作应该是什么。我当然没有问题只是在纸上或白板上涂写东西,并使用它来映射对这个共享对象的访问。但是,用UML图表做它会很好。我通常使用UML,但它在视觉上对我有意义,而不是总是“UML方式”。我几乎只使用简单的图表,如状态,类,活动和通信图表。我不确定哪些其他可用的最适合映射多个线程+共享内存。任何人都可以提供他们的建议和/或建模这类事情的经验吗?
答案 0 :(得分:1)
我使用时间轴可视化指令排序。让我们考虑以下代码。
x++;
y++;
所以我们的指示会像这样分解。
Read(x) --> Increment(x) --> Write(x) --> Read(y) --> Increment(y) --> Write(y)
为简洁起见,我们将其缩短为以下内容。
R(x) --> I(x) --> W(x) --> R(y) --> I(y) --> W(y)
因此,如果我们有2个线程执行这些指令,那么我们可以绘制2个时间轴,如此。
T1: R(x) --> I(x) --> W(x) --> R(y) --> I(y) --> W(y)
T2: R(x) --> I(x) --> W(x) --> R(y) --> I(y) --> W(y)
现在这是一次巨大的飞跃。当您从另一个线程的角度绘制可能的时间轴时。这很重要,因为指令可以从另一个线程(具有约束 1 )的角度重新排序。因此,下图是T1
的完全有效的执行序列,如从另一个线程所见。
T1: R(y) --> R(x) --> I(x) --> I(y) --> W(y) --> W(x)
现在让我们添加[
和]
字符分别代表锁定获取和锁定释放。 [
无法移动到另一个[
,并且无法在]
之前移动。类似的重新排序约束适用于]
字符。同样重要的是,没有任何指令可以移过[
或]
,但它们仍然可以在锁定部分内自由移动。最终的结果是锁序列化指令的执行(你已经知道)并且它们在某种程度上阻止了重新排序(你可能不知道)。
T1: [ -> R(x) ------> I(x) --> W(x) ---> ] ----------------------------------------------------------------------------> [ -----> R(y) -> I(y) --> W(y) -----> ]
T2: -------------------------------------> [ -> R(x) -----> I(x) ---> W(x) ----> ] -> [ --> R(y) --> I(y) --> W(y) -> ]
因此,当您绘制时间轴时,-->
(指令之间的间距)可以任意延长或缩短,以模拟线程的交错。指令延迟的长度将高度依赖于线程调度程序,使用的同步机制等,并且通常是非常随机的。
尝试在时间轴上左右移动指令,以帮助您查看可能发生的情况。为锁定,指令和延迟创建自己的符号和缩写,以帮助实现可视化,并提供有关将每个元素向左或向右移动的距离的线索。
1 实际上,相同变量的读取不会超过写入。这应该是直观的,但将形式化约束并没有什么坏处。