我有一个包含整数ID值的文件。目前,阅读该文件受ReaderWriterLockSlim
保护:
public int GetId()
{
_fileLock.EnterUpgradeableReadLock();
int id = 0;
try {
if(!File.Exists(_filePath))
CreateIdentityFile();
FileStream readStream = new FileStream(_filePath, FileMode.Open, FileAccess.Read);
StreamReader sr = new StreamReader(readStream);
string line = sr.ReadLine();
sr.Close();
readStream.Close();
id = int.Parse(line);
return int.Parse(line);
}
finally {
SaveNextId(id); // increment the id
_fileLock.ExitUpgradeableReadLock();
}
}
问题是GetId()
之后的后续操作可能会失败。正如您所看到的,GetId()
方法每次都会增加ID,而忽略了在发出ID后发生的情况。发布的ID可能会挂起(如上所述,可能会发生异常)。随着ID的增加,某些ID可能会被闲置。
所以我在考虑移动SaveNextId(id)
,删除它(SaveNextId()
实际上也使用了锁,除了它是EnterWriteLock
)。在执行所有必需的方法后,从外部手动调用它。这带来了另一个问题 - 多个线程可能会在执行GetId()
之前进入SaveNextId()
方法,并且它们可能都会收到相同的ID。
我不想要任何解决方案,我必须在操作后更改ID,以任何方式纠正它们,因为这样做并不好,可能会导致更多问题。
我需要一个解决方案,我可以以某种方式回调到FileIdentityManager(这是处理这些ID的类)并让管理员知道它可以执行保存下一个ID然后释放包含该文件的文件的读锁定ID。
Essentialy我想复制关系数据库自动增量行为 - 如果在插入行期间出现任何问题,则不使用ID,它仍可供使用,但也不会发生相同的ID。希望这个问题足以让你提供一些解决方案..
更新:有关我想要的行为的详细信息,请参阅答案评论
答案 0 :(得分:2)
private static readonly object _lock = new object();
public int GetId()
{
lock(_lock)
{
//You code to get ID here
}
}
答案 1 :(得分:2)
Essentialy我想要复制 关系数据库自动增量 行为 - 如果出现任何问题 在行插入期间, ID不是 使用过,仍然可以使用 但它也永远不会发生 发出相同的ID。希望如此 问题是可以理解的 你提供一些解决方案。
一般来说,这不是我观察到的行为。当您在事务中插入一个自动增量并且已回滚的行时,您丢失了该ID。
所以在我看来,你实现这个的方式是正确的行为。
<强>更新强> 唯一可以确保“不想将它们浪费在不成功的文件保存,不成功的类型转换等上”的方法。从您请求新ID到保存完成以及无法将增量回滚到ID时,是否要将阻止代码的范围更改为阻止。
这将大大降低您可以实现的并行度。
如果您希望保持更高的并行度,您应该在申请ID之前检查所有内容,例如:检查类型和格式错误。
显然有些事情比如外部错误(IO异常)你根本无法做任何事情。
答案 2 :(得分:0)
您在数据库中看到的行为是可能的,因为ID生成和行插入是原子的。如果您希望在应用程序中出现此行为,那么我建议您在存储数据之前立即获取该ID。这会将您的“交易范围”减少到可能的最小窗口,并且应该防止任何例外干扰。
如果出于某种不良原因,这是不可能的,另一种方法可能是拥有一个缓存ID计数器的“ID代理”。它将从文件中读取当前计数器,将其递增一些数字(比如100),然后通过单线程方法将连续ID分发给所有调用者。当它分发全部100时,它会再次更新文件。在关机时,它会使用它发出的最后一个值最后一次写入文件。唯一的问题是,如果你的系统崩溃,你的ID会有差距,但有办法弥补这一点。