我有一个在网络上运行的桌面应用程序,每个实例都连接到同一个数据库。
那么,在这种情况下,如何实现一个互斥体,该互斥体适用于连接到同一数据库的所有正在运行的实例?
换句话说,我不希望那两个+实例同时运行相同的功能。如果一个人已经在运行该功能,则其他实例不应该有权访问它。
PS:数据库事务无法解决,因为我不想使用mutex这个函数不使用数据库。我之前提到数据库只是因为它可以用来在运行的实例之间交换信息。
PS2:该函数大约需要30分钟才能完成,所以如果第二个实例尝试运行相同的函数,我想显示一条好消息,它现在无法执行,因为计算机'X'已经是运行该功能。
PS3:必须在客户端计算机上处理该函数,因此我无法使用存储过程。
答案 0 :(得分:2)
我认为您正在寻找数据库事务。事务将使您的更改与所有其他客户端隔离。
更新: 您提到该函数当前未写入数据库。如果要对此函数进行互斥,则必须存在一些中心位置来存储当前的互斥锁。数据库可以为此工作 - 只需添加一个包含当前持有者的计算机名称的新表。在开始功能之前检查该表。
我认为你的问题可能会引起混淆。互斥体应该是关于保护资源。如果您的功能没有访问数据库,那么您要保护哪些共享资源?
答案 1 :(得分:0)
将代码放在事务中 - 在应用程序中,或者更好 - 在存储过程中,并调用存储过程。 事务机制将隔离调用者之间的代码。
答案 2 :(得分:0)
反过来考虑一个消息队列。如上所述,DB应该在事务或对表的串行访问(ala MyISAM)中为您管理所有这些。
答案 3 :(得分:0)
过去我做过以下事情:
我不知道您使用的是哪种RDBMS,但大多数都有办法锁定单个记录以进行更新。这是一些基于Oracle的pseduocode:
BEGIN TRANS
SELECT FOR UPDATE is_running FROM function_table WHERE function_name ='foo';
- 检查此处是否正在运行,如果没有,您可以将运行设置为“true”
UPDATE function_table set is_running ='Y'其中function_name ='foo';
COMMIT TRANS
现在我没有Oracle PSQL文档,但你明白了。 'FOR UPDATE'子句在读取之后锁定记录,直到提交为止,因此其他进程将阻塞该SELECT语句,直到当前进程提交为止。
答案 4 :(得分:0)
如果您有Java堆栈,可以使用Terracotta来实现此类功能。
答案 5 :(得分:0)
即使您的函数当前没有使用数据库,您仍然可以使用特定的表来解决问题,以便同步此函数。具体取决于您的数据库以及它如何处理隔离级别和锁定。例如,使用SQL Server,您可以将事务隔离设置为可重复读取,从锁定行读取值并在事务内更新它。在功能完成之前不要提交事务。您还可以在大多数数据库的事务中使用显式表锁,这可能更简单。鉴于您已经在使用数据库,这可能是最简单的解决方案。
如果您不想因任何原因依赖数据库,您可以编写一个接受来自客户端的TCP连接的简单服务。每个客户端都会请求运行权限,并在完成后返回响应。服务器将能够确保一次只有一个客户端获得运行权限。只要您具有正确的保持活动设置,死客户端最终将丢弃TCP连接并被检测到。
Xepoch建议的消息队列解决方案也可以使用。您可以使用类似MSMQ或Java Message Queue的内容,并使用一条消息作为运行令牌。您的所有客户都会请求该消息,然后在完成后重新发布。如果客户端在重新发布之前就已经死了,那么您将面临死锁的风险,因此您需要设计一些逻辑来检测它并且可能会变得复杂。