我有一个应用程序将从文件夹中读取并等待文件出现在此文件夹中。当此文件出现时,应用程序应读取内容,使用文件中的数据向外部系统执行一些功能,然后删除文件(然后等待下一个文件)。
现在,我想在两台不同的机器上运行此应用程序,但两者都在同一个文件夹中监听。所以它是完全相同的应用程序,但有两个实例。我们称之为实例A和实例B.
因此,当出现新文件时,A和B都会找到该文件,并且两者都会尝试读取它。这将导致两个实例之间出现某种竞争条件。我希望如果A开始在B之前读取文件,B只需跳过该文件并让A进程并删除它。同样的事情,如果B先找到文件,A就什么都不做。
现在我怎么能实现这一点,设置锁定文件是不够的我猜是因为让我们说A开始读取文件,然后由A锁定,然后A将解锁它以便删除它。在此期间,B可能会尝试读取该文件。在这种情况下,文件被处理两次,这是不可接受的。
总而言之,每当文件出现在文件夹中时,我就会有一个程序和一个文件夹/网络共享的两个实例。我希望EITHER实例A或实例B处理该文件。从来没有,关于如何在C#中实现这样的功能的任何想法?
答案 0 :(得分:0)
我可以想到两个快速解决方案;
分发负载
拥有2个进程,以便它们只能处理某些文件。如何执行此操作可能基于文件名或日期/时间。例如。进程1读取时间戳以奇数结尾的文件,进程2读取具有偶数的文件。
数据库锁定
另一种选择是你使用某种数据库作为锁 进程1读取文件并根据文件名插入数据库表(必须是唯一的)。如果插入有效,那么它负责该文件并继续处理它,否则如果插入失败,则另一个进程已经插入它以便它负责并且进程1忽略该文件。
两个进程都必须可以访问数据库,这会产生一些开销。但如果您想将其扩展到更多流程,则可能是更好的选择。
答案 1 :(得分:0)
执行此操作的正确方法是使用写锁定打开文件(例如,System.IO.FileAccess.Write和读取共享(例如,System.IO.FileShare.Read)。如果其中一个进程尝试在另一个进程打开文件时打开该文件,然后open命令将抛出一个异常,您需要根据需要捕获并处理该异常(例如,log和retry)。通过对文件使用写锁定打开,你保证开启和锁定是原子的,因此在两个进程之间同步,并且没有竞争条件。
这样的事情:
try
{
using (FileStream fileStream = new FileStream(FileName, FileMode.Open, FileAccess.Write, FileShare.Read))
{
// Read from or write to file.
}
}
catch (IOException ex)
{
// The file is locked by the other process.
// Some options here:
// Log exception.
// Ignore exception and carry on.
// Implement a retry mechanism to try opening the file again.
}
如果您希望其他进程在程序打开时根本无法访问该文件,则可以使用FileShare.None。我更喜欢FileShare.Read,因为它允许我监视文件中发生的事情(例如,在记事本中打开它)。
为了满足删除文件的原则是一个类似的原则:首先重命名/移动文件并捕获在其他进程已经重命名/移动它时发生的IOException,然后打开重命名/移动的文件。您重命名/移动文件以指示文件已在处理中,并且应由其他进程忽略。例如,使用.pending文件扩展名重命名,或将其移动到Pending目录。
try
{
// This will throw an exception if the other process has already moved the file -
// either FileName no longer exists, or it is locked.
File.Move(FileName, PendingFileName);
// If we get this far we know we have exclusive access to the pending file.
using (FileStream fileStream = new FileStream(PendingFileName, FileMode.Open, FileAccess.Write, FileShare.Read))
{
// Read from or write to file.
}
File.Delete(PendingFileName);
}
catch (IOException ex)
{
// The file is locked by the other process.
// Some options here:
// Log exception.
// Ignore exception and carry on.
// Implement a retry mechanism to try moving the file again.
}
与打开文件一样,File.Move是原子的并受锁保护,因此可以保证,如果有多个并发线程/进程尝试移动文件,则只有一个会成功,其他的会抛出异常。请点击此处查看类似问题:Atomicity of File.Move。
答案 2 :(得分:0)
相反,我会建议使用功能 - 服务器方法来深入了解文件访问权限。此方法的其他参数是来自不同计算机的文件使用。在访问和权限管理方面,这一点非常重要。
我的建议是要有一个单点文件访问(文件存储库),它实现了以下功能:
有很多方法可以实现这种方法。 (使用文件的API和文件版本控制系统;实现服务;使用数据库,......)
一个简单的(需要一个支持事务,触发器或存储过程的数据库)
答案 3 :(得分:0)
因此,如果您要应用锁定,可以尝试使用文件名作为锁定对象。您可以尝试以特殊方式重命名文件(例如通过在文件名前添加点) 并且第一个幸运重命名文件的服务将继续使用它。第二个(慢)将获得文件不存在的异常。
你必须在你的文件处理逻辑中加上检查,服务不会试图“锁定”已经“锁定”的文件(名字以点开头)。
UPD可能最好包含一组特殊字符(如标记)和一些服务标识符(与PID连接的机器名)
因为我不确定文件重命名在并发模式下是如何工作的。
因此,如果共享文件夹中有file.txt
.lock
- 特殊标记,Devhost
- 当前计算机的名称和345
是一个PID(进程)标识符)file.txt.lockDevhost345
个文件
可用如果是 - 它被当前服务实例锁定并可以使用 如果不是 - 它被并发服务“窃取”,因此不应该被处理。
如果您没有写入权限,则可以使用其他网络共享并尝试创建其他文件锁定标记,例如file.txt
服务可以尝试创建(并保持写锁定)新文件,如{{1已创建锁定文件的第一个服务正在处理原始文件,并仅在处理原始文件时删除锁定。