如何跨网络互斥?
我有一个在网络上运行的桌面应用程序,每个实例都连接到同一个数据库。
那么,在这种情况下,如何实现在连接到同一数据库的所有正在运行的实例上工作的互斥体?
换句话说,我不希望两个以上实例同时运行相同的函数。如果一个实例已经在运行该函数,则其他实例不应访问它。
PS:数据库事务无法解决,因为我不想互斥的函数不使用数据库。我提到数据库只是因为它可用于在运行的实例之间交换信息。
PS2:该功能大约需要 30 分钟才能完成,因此如果第二个实例尝试运行相同的功能,我想显示一条很好的消息,表明它现在无法执行,因为计算机“X”已经在运行该功能。
PS3:该函数必须在客户端计算机上处理,所以我不能使用存储过程。
I have a desktop application that runs on a network and every instance connects to the same database.
So, in this situation, how can I implement a mutex that works across all running instances that are connected to the same database?
In other words, I don't wan't that two+ instances to run the same function at the same time. If one is already running the function, the other instances shouldn't have access to it.
PS: Database transaction won't solve, because the function I wan't to mutex doesn't use the database. I've mentioned the database just because it can be used to exchange information across the running instances.
PS2: The function takes about ~30 minutes to complete, so if a second instance tries to run the same function I would like to display a nice message that it can't be performed right now because computer 'X' is already running that function.
PS3: The function has to be processed on the client machine, so I can't use stored procedures.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(6)
我认为您正在寻找数据库事务。事务会将您的更改与所有其他客户端隔离。
更新:
您提到该函数当前不写入数据库。如果你想互斥这个函数,就必须有一些中心位置来存储当前的互斥持有者。数据库可以解决这个问题——只需添加一个包含当前持有者计算机名的新表即可。在开始您的功能之前检查该表。
我认为你的问题可能很混乱。互斥锁应该是为了保护资源。如果您的函数不访问数据库,那么您要保护什么共享资源?
I think you're looking for a database transaction. A transaction will isolate your changes from all other clients.
Update:
You mentioned that the function doesn't currently write to the database. If you want to mutex this function, there will have to be some central location to store the current mutex holder. The database can work for this -- just add a new table that includes the computername of the current holder. Check that table before starting your function.
I think your question may be confusion though. Mutexes should be about protecting resources. If your function is not accessing the database, then what shared resource are you protecting?
将代码放在事务中 - 在应用程序中,或者更好 - 在存储过程中,然后调用存储过程。
事务机制将隔离调用者之间的代码。
put the code inside a transaction either - in the app, or better -inside a stored procedure, and call the stored procedure.
the transaction mechanism will isolate the code between the callers.
相反地考虑消息队列。如前所述,数据库应该在事务或对表的串行访问(ala MyISAM)中为您管理所有这些。
Conversely consider a message queue. As mentioned, the DB should manage all of this for you either in transactions or serial access to tables (ala MyISAM).
过去我做了以下工作:
我不知道您使用的是什么 RDBMS,但大多数都有一种方法来锁定单个记录以进行更新。这是一些基于Oracle的伪代码:
开始传输
选择更新 is_running FROM function_table WHERE function_name='foo';
--检查这里是否正在运行,如果没有,可以将running设置为'true'
更新 function_table set is_running='Y' where function_name='foo';
COMMIT TRANS
现在我没有带 Oracle PSQL 文档,但您明白了。 “FOR UPDATE”子句在读取后锁定该记录直到提交,因此其他进程将阻塞该 SELECT 语句,直到当前进程提交。
In the past I have done the following:
I don't know what RDBMS you are using, but most have a way to lock individual records for update. Here is some pseduocode based on Oracle:
BEGIN TRANS
SELECT FOR UPDATE is_running FROM function_table WHERE function_name='foo';
-- Check here to see if it is running, if not, you can set running to 'true'
UPDATE function_table set is_running='Y' where function_name='foo';
COMMIT TRANS
Now I don't have the Oracle PSQL docs with me, but you get the idea. The 'FOR UPDATE' clause locks there record after the read until the commit, so other processes will block on that SELECT statement until the current process commits.
如果您有 Java 堆栈,则可以使用 Terracotta 来实现此类功能。
You can use Terracotta to implement such functionality, if you've got a Java stack.
即使您的函数当前不使用数据库,您仍然可以使用特定表来解决问题,以同步该函数。具体细节取决于您的数据库及其处理隔离级别和锁定的方式。例如,使用 SQL Server,您可以将事务隔离设置为可重复读取,从锁定行读取值并在事务内更新它。在您的函数完成之前不要提交事务。您还可以在大多数数据库的事务中使用显式表锁,这可能更简单。考虑到您已经在使用数据库,这可能是最简单的解决方案。
如果您出于某种原因不想依赖数据库,您可以编写一个简单的服务来接受来自客户端的 TCP 连接。每个客户端都会请求运行权限,并在完成后返回响应。服务器将能够确保一次只有一个客户端获得运行权限。只要您有正确的保持活动设置,失效的客户端最终就会断开 TCP 连接并被检测到。
Xepoch 建议的消息队列解决方案也可以工作。您可以使用 MSMQ 或 Java 消息队列之类的东西,并拥有一条充当运行令牌的消息。您的所有客户都会请求该消息,然后在完成后重新发布。如果客户端在重新发布之前死亡,您将面临僵局的风险,因此您需要设计一些逻辑来检测这一点,并且可能会变得复杂。
Even if your function does not currently use the database, you could still solve the problem with a specific table for the purpose of synchronizing this function. The specifics would depend on your DB and how it handles isolation levels and locking. For example, with SQL Server you would set the transaction isolation to repeatable read, read a value from your locking row and update it inside a transaction. Don't commit the transaction until your function is done. You can also use explicit table locks in a transaction on most databases which might be simpler. This is probably the simplest solution given you are already using a database.
If you do not want to rely on the database for whatever reason you could write a simple service that would accept TCP connections from your client. Each client would request permission to run and would return a response when done. The server would be able to ensure only one client gets permission to run at a time. Dead clients would eventually drop the TCP connection and be detected as long as you have the correct keep alive setting.
The message queue solution suggested by Xepoch would also work. You could use something like MSMQ or Java Message Queue and have a single message that would act as a run token. All your clients would request the message and then repost it when done. You risk a deadlock if a client dies before reposting so you would need to devise some logic to detect this and it might get complicated.