活锁的好例子?
我了解活锁是什么,但我想知道是否有人有一个很好的基于代码的示例? 通过基于代码,我并不是指“两个人试图在走廊里超越对方”。 如果我再读一遍,我就会失去午餐。
I understand what livelock is, but I was wondering if anyone had a good code-based example of it? And by code-based, I do not mean "two people trying to get past each other in a corridor". If I read that again, I'll lose my lunch.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(12)
这是一个非常简单的 Java 活锁示例,丈夫和妻子试图喝汤,但他们之间只有一把勺子。 夫妻双方都太有礼貌了,如果对方还没有吃饭,就会把勺子递过去。
运行该程序,您将得到:
如果不间断,这将永远持续下去。 这是一个活锁,因为 Alice 和 Bob 都在无限循环中反复要求对方先走(因此live)。 在死锁情况下,Alice 和 Bob 都会被冻结,等待对方先走——除了等待之外,他们不会做任何事情(因此死了)。
Here's a very simple Java example of livelock where a husband and wife are trying to eat soup, but only have one spoon between them. Each spouse is too polite, and will pass the spoon if the other has not yet eaten.
Run the program and you'll get:
This will go on forever if uninterrupted. This is a livelock because both Alice and Bob are repeatedly asking each other to go first in an infinite loop (hence live). In a deadlock situation, both Alice and Bob would simply be frozen waiting on each other to go first — they won't be doing anything except wait (hence dead).
撇开轻率的评论不谈,已知出现的一个例子是尝试检测和处理死锁情况的代码。 如果两个线程检测到死锁,并尝试互相“让开”,那么如果不小心,它们最终会陷入一个始终“让开”的循环中,并且永远无法继续前进。
我所说的“靠边站”是指他们将释放锁并尝试让另一个人获取它。 我们可以想象两个线程执行此操作的情况(伪代码):
除了竞争条件之外,我们这里遇到的情况是两个线程如果同时进入,最终将在内部循环中运行而不继续进行。 显然这是一个简化的例子。 一个天真的修复方法是在线程等待的时间中加入某种随机性。
正确的解决方法是始终遵循锁层次结构。 选择获取锁的顺序并遵守该顺序。 例如,如果两个线程总是在lock2之前获取lock1,那么就不可能出现死锁。
Flippant comments aside, one example which is known to come up is in code which tries to detect and handle deadlock situations. If two threads detect a deadlock, and try to "step aside" for each other, without care they will end up being stuck in a loop always "stepping aside" and never managing to move forwards.
By "step aside" I mean that they would release the lock and attempt to let the other one acquire it. We might imagine the situation with two threads doing this (pseudocode):
Race conditions aside, what we have here is a situation where both threads, if they enter at the same time will end up running in the inner loop without proceeding. Obviously this is a simplified example. A naiive fix would be to put some kind of randomness in the amount of time the threads would wait.
The proper fix is to always respect the lock heirarchy. Pick an order in which you acquire the locks and stick to that. For example if both threads always acquire lock1 before lock2, then there is no possibility of deadlock.
由于没有答案标记为接受的答案,我尝试创建活锁示例;
原始程序是我于 2012 年 4 月编写的学习多线程的各种概念。 这次我修改了它以创建死锁、竞争条件、活锁等。
所以让我们先了解问题陈述;
Cookie Maker 问题
有一些配料容器:ChocoPowederContainer、WheatPowderContainer。 CookieMaker 从配料容器中取出一些粉末来烘烤Cookie。 如果饼干制造商发现一个容器是空的,它会检查另一个容器以节省时间。 并等待填充器填充所需的容器。 有一名灌装员定期检查容器并在容器需要时填充一定数量。
请在 github 上查看完整代码;
让我简要解释一下您的实施情况。
让我们看一下代码:
CookieMaker.java
IngredientContainer.java
一切都运行良好,直到 Filler< /strong> 正在填充容器。 但是,如果我忘记启动灌装机,或者灌装机意外休假,子线程会不断更改其状态,以允许其他制造商去检查容器。
我还创建了一个守护进程 ThreadTracer 监视线程状态和死锁。 这是控制台的输出;
您会注意到子线程并更改其状态并等待。
As there is no answer marked as accepted answer, I have attempted to create live lock example;
Original program was written by me in Apr 2012 to learn various concept of multithreading. This time I have modified it to create deadlock, race condition, livelock etc.
So let's understand the problem statement first;
Cookie Maker Problem
There are some ingredient containers: ChocoPowederContainer, WheatPowderContainer. CookieMaker takes some amount of powder from ingredient containers to bake a Cookie. If a cookie maker finds a container empty it checks for another container to save time. And waits until Filler fills the required container. There is a Filler who checks container on regular interval and fills some quantity if a container needs it.
Please check the complete code on github;
Let me explain you implementation in brief.
Let's have a look in the code:
CookieMaker.java
IngredientContainer.java
Everything runs fine until Filler is filling the containers. But if I forget to start the filler, or filler goes on unexpected leave, sub-threads keep changing their states to allow other maker to go and check the container.
I have also create a daemon ThreadTracer which keeps watch on thread states and deadlocks. This the output from console;
You'll notice that sub-threads and changing their states and waiting.
一个真实的(尽管没有确切的代码)示例是两个竞争进程实时锁定,试图纠正 SQL Server 死锁,每个进程使用相同的等待重试算法进行重试。 虽然时机很幸运,但我已经看到这种情况发生在具有相似性能特征的不同机器上,以响应添加到 EMS 主题的消息(例如多次保存单个对象图的更新),并且无法控制锁定顺序。
在这种情况下,一个好的解决方案是拥有竞争的消费者(通过将工作划分到不相关的对象上,尽可能防止在链的高层进行重复处理)。
一个不太理想的(好吧,肮脏的黑客)解决方案是提前打破定时坏运气(处理中的一种强制差异),或者通过使用不同的算法或某些随机性元素在死锁后打破它。 这仍然可能存在问题,因为每个进程的锁获取顺序可能是“粘性的”,并且这需要等待重试中未考虑到的某个最短时间。
另一种解决方案(至少对于 SQL Server 而言)是尝试不同的隔离级别(例如快照)。
A real (albeit without exact code) example is two competing processes live locking in an attempt to correct for a SQL server deadlock, with each process using the same wait-retry algorithm for retrying. While it's the luck of timing, I have seen this happen on separate machines with similar performance characteristics in response to a message added to an EMS topic (e.g. saving an update of a single object graph more than once), and not being able to control the lock order.
A good solution in this case would be to have competing consumers (prevent duplicate processing as high up in the chain as possible by partitioning the work on unrelated objects).
A less desirable (ok, dirty-hack) solution is to break the timing bad luck (kind of force differences in processing) in advance or break it after deadlock by using different algorithms or some element of randomness. This could still have issues because its possible the lock taking order is "sticky" for each process, and this takes a certain minimum of time not accounted for in the wait-retry.
Yet another solution (at least for SQL Server) is to try a different isolation level (e.g. snapshot).
我编写了两个人在走廊经过的示例。 一旦两个线程意识到它们的方向相同,它们就会互相避开。
I coded up the example of 2 persons passing in a corridor. The two threads will avoid each other as soon as they realise their directions are the same.
jelbourn 代码的 C# 版本:
C# version of jelbourn's code:
考虑一个具有 50 个进程槽的 UNIX 系统。
十个程序正在运行,每个程序必须创建 6 个(子)进程。
每个进程创建了4个进程后,原来的10个进程和新的40个进程就耗尽了该表。 现在,10 个原始进程中的每一个都处于无限循环分叉和失败中——这正是活锁的情况。 这种情况发生的可能性很小,但有可能发生。
Consider a UNIX system having 50 process slots.
Ten programs are running, each of which having to create 6 (sub)processes.
After each process has created 4 processes, the 10 original processes and the 40 new processes have exhausted the table. Each of the 10 original processes now sits in an endless loop forking and failing – which is aptly the situation of a livelock. The probability of this happening is very little but it could happen.
这里的一个例子可能是使用定时的 tryLock 来获取多个锁,如果无法获取全部锁,请退出并重试。
我可以想象这样的代码会有问题,因为有很多线程发生冲突并等待获取一组锁。 但我不确定这个简单的例子对我来说是否非常有吸引力。
One example here might be using a timed tryLock to obtain more than one lock and if you can't obtain them all, back off and try again.
I could imagine such code would be problematic as you have lots of threads colliding and waiting to obtain a set of locks. But I'm not sure this is very compelling to me as a simple example.
jelbourn 代码的 Python 版本:
Python version of jelbourn's code:
我修改了@jelbourn的答案。
当其中一个注意到另一个饿了时,他(她)应该释放勺子并等待另一个通知,因此发生了活锁。
I modify the answer of @jelbourn.
When one of them notices that the other is hungry, he(her) should release the spoon and wait another notify, so a livelock happens.
示例:
线程 1
线程 2
唯一的区别是线程 1 和线程 2 尝试以不同的顺序获取锁。 活锁可能发生如下:
线程 1 运行获取 L1,然后发生上下文切换。 线程 2 运行获取 L2,然后发生另一个上下文切换。 线程 1 运行并且无法获取 L2,但在释放 L1 之前发生上下文切换。 线程2运行无法获取L1,释放L2,发生上下文切换。 线程1释放了L1,现在我们基本上回到了起始状态,理论上这些步骤可以永远重复。
Example:
Thread 1
Thread 2
The only difference is Thread 1 and Thread 2 try to acquire the locks in a different order. Livelock could happen as follows:
Thread 1 runs acquires L1, then a context switch occurs. Thread 2 runs acquires L2, then another context switch occurs. Thread 1 runs and cannot acquire L2, but before releasing L1 a context switch occurs. Thread 2 runs and cannot acquire L1, releases L2, and a context switch occurs. Thread 1 releases L1, and now we are basically back to the starting state, and in theory these steps could keep repeating forever.