2011-06-24 26 views
0

我有一个包含整数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)。在完成所有必需的方法执行后,从外部手动调用它。这带来了另一个问题 - 在执行SaveNextId()之前,多个线程可能会输入GetId()方法,并且它们可能都会收到相同的ID。

我不希望任何解决方案,在操作后必须更改ID,并以任何方式纠正它们,因为这样做不好,可能会导致更多问题。

我需要一个解决方案,我可以以某种方式回调到FileIdentityManager(这是处理这些ID的类),并让管理员知道它可以执行保存下一个ID然后释放包含文件的读锁定ID。

Essentialy我想复制关系数据库的自动增量行为 - 如果在行插入过程中出现任何错误,这个ID不被使用,它仍然可以使用,但它也不会发生相同的ID发出。希望这个问题是可以理解的不足,为您提供一些解决办法..

UPDATE:请参见注释给答案有关行为的详细信息我想

+0

我认为你有非顺序的ID是一件坏事吗? – Chris

+0

如果你想要一个只允许一个线程的锁,为什么不使用'lock'?我只是阅读标题而不是问题! –

+0

我会处理因删除而发生的问题。但我不想把它们浪费在没有成功的文件保存上,没有成功的类型转换等等。我现在就开始工作了,但这个非序列ID问题很快就显示出来了,因为ID暴露在UI中,它看起来很难看。两个并发内容项目可能具有相隔10个数字或更多的ID。 – mare

回答

2

Essentialy我想复制 关系数据库自动增量 行为 - 如果有什么行插入时错误的ID是不 使用,它仍然是AV可供使用 但它也不会发生 相同的ID发布。希望 的问题是可以理解的,你可以提供一些解决方案。

一般来说,这不是我观察到的行为。当您在事务内部使用自动增量向表中插入一行并将其回滚时,您已丢失该ID。

所以在我看来,你实施这种方式是正确的行为。

更新 你能保证你的唯一办法“不想浪费他们不成功的文件保存,不成功的类型转换,等等。”在你请求一个新的ID直到你的保存完成并且未能将增量回滚到ID的时候,改变你的阻塞代码的范围以阻止它。

这将大大减少并行可以达到的水平。

如果你想保持并行性的潜力更高,你应该检查你的一切,你可以要求例如一个ID之前检查类型和格式错误。

显然,有些事情就像外部错误(IO异常),你根本无法做任何事情。

+0

好吧,这可能是真的我想(应该去检查一遍),但现在我真的想解决我的问题,因为它应该是可能的,特别是因为我们控制行为,在SQL Server中我们不能做太多的事情它... – mare

+0

好吧,我现在明白你好一点了。我决定使用@WraithNath建议的锁,但在存储库中执行。锁块将包含try..catch块,try将包含GetId(),其他调用和SaveNextId()。使用ReaderWriterLockSlim,GetId()和SaveNextId()将保持现在的状态。但是,我有一个问题。这是如何影响的,因为这是一个ASP.NET MVC应用程序,如果你知道我的意思,我也使用Ninject创建具有InRequestScope()选项的存储库。 – mare

2
private static readonly object _lock = new object(); 

    public int GetId() 
    { 
     lock(_lock) 
     { 
     //You code to get ID here 
     } 
    } 
+0

我猜想当代码获得ID完成锁立即解锁和另一个线程可以进入?可能在我们知道发生了什么事情之前呢? – mare

+0

是的,当代码运行在锁范围之外时,代码将解锁并允许另一个线程进入。你可以在锁里放入任何你想要的逻辑来计算你的下一个id是什么,以及是否保存它。但只要在整个应用程序中使用此方法,一次只能有一个线程通过它。 – WraithNath

+0

事情是我的GetId()方法从处理数据访问和插入的存储库中调用。它从FileIdentityManager类请求一个ID,然后构造将被序列化为XML的对象(包括ID分配)。在序列化过程中发生异常时,可以回滚ID。 – mare

0

您在数据库中看到的行为是可能的,因为ID生成和插入行是原子的。如果你想在你的应用程序中有这种行为,那么我建议你在存储数据之前立即获取ID。这会将您的“交易范围”降低到最小的窗口,并且应该防止任何异常干扰。

如果由于某些原因不好这是不可能的,另一种可能是有一个“ID经纪人”该缓存ID计数器。它会从文件读取当前计数器,将其增加一些数字(比如100),然后通过单线程方法将连续的ID分配给所有的调用者。当它发布了全部100个时,它会再次更新文件。在关机时,它会最后一次使用它发出的最后一个值写入文件。唯一的问题就是如果你的系统崩溃了,你的ID会出现一个缺口,但是有办法弥补这个缺陷。

相关问题