如何创建一个只允许一个线程从资源中读取的锁?

时间:2011-06-24 14:46:11

标签: c# .net

我有一个包含整数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。希望这个问题足以让你提供一些解决方案..

更新:有关我想要的行为的详细信息,请参阅答案评论

3 个答案:

答案 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会有差距,但有办法弥补这一点。