如何在C#中锁定整数?

有没有办法在C#中锁定一个整数? 整数不能与锁一起使用,因为它们是盒装的(并且只锁定引用上的锁)。

场景如下:我有一个基于论坛的网站,具有审核function。 我想要做的是确保在任何给定时间只有一个主持人可以调节post。 为此,我想锁定post的ID。

到目前为止我有几个想法(例如使用字典),但我正在寻找一种更好,更清洁的方法。

有什么建议?

我喜欢这样做

public class Synchronizer { private Dictionary locks; private object myLock; public Synchronizer() { locks = new Dictionary(); myLock = new object(); } public object this[int index] { get { lock (myLock) { object result; if (locks.TryGetValue(index, out result)) return result; result = new object(); locks[index] = result; return result; } } } } 

然后,简单地锁定一个int(每次使用相同的同步器)

 lock (sync[15]) { ... } 

当给出相同的索引两次时,此类返回相同的锁对象。 当一个新索引到来时,它会创建一个对象,返回它,然后将它存储在字典中以供下次使用。

它可以很容易地更改为使用任何struct或值类型一般工作,或者是static以便不必传递同步器对象。

如果它是一个网站,那么使用进程内锁可能不是最好的方法,就好像你需要将网站扩展到多个服务器,或添加另一个托管API的网站(或任何其他需要其他进程访问相同的网站)数据存在)然后你所有的锁定策略立即无效。

我倾向于研究基于数据库的锁定。 最简单的方法是使用乐观锁定,例如上次更新post的时间戳,以及拒绝对post进行的更新,除非时间戳匹配。

我已经阅读了很多评论,提到锁定对Web应用程序来说并不安全,但除了Web场之外,我还没有看到任何解释原因。 我有兴趣听取反对它的论点。

我有类似的需求,虽然我在硬盘上缓存重新resize的图像(这显然是一个本地操作,因此Web场景不是问题)。

这是@Configurator发布的重做版本。 它包含一些@Configurator不包含的function:

  1. 解锁:确保列表不会增长到不合理的大(我们有数百万张照片,每个都可以有不同的大小)。
  2. 通用:允许基于不同的数据类型(例如int或string)进行锁定。

这是代码……

 ///  /// Provides a way to lock a resource based on a value (such as an ID or path). ///  public class Synchronizer { private Dictionary mLocks = new Dictionary(); private object mLock = new object(); ///  /// Returns an object that can be used in a lock statement. Ex: lock(MySync.Lock(MyValue)) { ... } ///  ///  ///  public SyncLock Lock(T value) { lock (mLock) { SyncLock theLock; if (mLocks.TryGetValue(value, out theLock)) return theLock; theLock = new SyncLock(value, this); mLocks.Add(value, theLock); return theLock; } } ///  /// Unlocks the object. Called from Lock.Dispose. ///  ///  public void Unlock(SyncLock theLock) { mLocks.Remove(theLock.Value); } ///  /// Represents a lock for the Synchronizer class. ///  public class SyncLock : IDisposable { ///  /// This class should only be instantiated from the Synchronizer class. ///  ///  ///  internal SyncLock(T value, Synchronizer sync) { Value = value; Sync = sync; } ///  /// Makes sure the lock is removed. ///  public void Dispose() { Sync.Unlock(this); } ///  /// Gets the value that this lock is based on. ///  public T Value { get; private set; } ///  /// Gets the synchronizer this lock was created from. ///  private Synchronizer Sync { get; set; } } } 

这是你如何使用它…

 public static readonly Synchronizer sPostSync = new Synchronizer(); .... using(var theLock = sPostSync.Lock(myID)) lock (theLock) { ... } 

我个人会选择格雷格或康拉德的方法。

如果你确实想要lockpostID本身(假设你的代码只能在一个进程中运行),那么这样的东西不会太脏:

 public class ModeratorUtils { private static readonly HashSet _LockedPosts = new HashSet(); public void ModeratePost(int postId) { bool lockedByMe = false; try { lock (_LockedPosts) { lockedByMe = _LockedPosts.Add(postId); } if (lockedByMe) { // do your editing } else { // sorry, can't edit at this time } } finally { if (lockedByMe) { lock (_LockedPosts) { _LockedPosts.Remove(postId); } } } } } 

此选项建立在配置程序提供的良好答案的基础上,具有以下修改:

  1. 防止字典的大小无法控制地增长。 因为,新的post将获得新的ID,你的锁定字典将无限增长。 解决方案是根据最大字典大小修改id。 这确实意味着一些id将具有相同的锁(并且必须等待它们否则不必),但这对于某些字典大小是可接受的。
  2. 使用ConcurrentDictionary,因此不需要单独的字典锁。

代码:

 internal class IdLock { internal int LockDictionarySize { get { return m_lockDictionarySize; } } const int m_lockDictionarySize = 1000; ConcurrentDictionary m_locks = new ConcurrentDictionary(); internal object this[ int id ] { get { object lockObject = new object(); int mapValue = id % m_lockDictionarySize; lockObject = m_locks.GetOrAdd( mapValue, lockObject ); return lockObject; } } } 

此外,为了完整性,还有字符串实习的替代方案: –

  1. 根据您允许的最大内部id字符串数修改id。
  2. 将此修改值转换为字符串。
  3. 将modded字符串与GUID或命名空间名称连接以实现名称冲突安全性。
  4. 实习生这个字符串。
  5. 锁定实习字符串。 有关一些信息,请参阅此答案 :

字符串实习方法的唯一好处是您不需要管理字典。 我更喜欢锁定词典方法,因为实习生方法对字符串实习如何工作以及它将继续以这种方式工作做出很多假设。 它还使用实习来做它从未意图/设计的事情。

你为什么不锁定整个post而只是锁定它的ID?

codeplex上的Coresystem有两个基于值类型的线程同步类,有关详细信息,请参阅http://codestand.feedbook.org/2012/06/lock-on-integer-in-c.html

我怀疑您是否应该使用数据库或O / S级别function(如锁定)来进行业务级别决策。 长时间保持锁定会产生很大的开销(在这些情况下,超过几百毫秒的任何事情都是永恒的)。

在post中添加状态字段。 如果你直接处理几个therads,那么你可以使用O / S级别锁定来设置标志。

你需要一个完全不同的方法。

请记住,对于一个网站,您实际上并没有在另一侧有一个实时运行的应用程序来响应用户的操作。

你基本上启动一个迷你应用程序,它返回网页,然后服务器完成。 用户最终发回一些数据是副产品,而非保证。

因此,您需要在应用程序将审核页面返回给主持人后锁定以保持持久性,然后在主持人完成后释放它。

并且你需要处理某种超时,如果主持人在获得审核页面后关闭了他的浏览器,从而永远不会与他/她完成该post的审核过程的服务器进行通信。

理想情况下,您可以避免所有复杂和脆弱的C#锁定并将其替换为数据库锁定,如果您的事务设计正确,那么您应该只能使用数据库事务。

碰巧具有相同值的两个盒装整数是完全独立的对象。 所以,如果你想这样做,你的词典概念可能就是这样。 您需要同步对字典的访问权限,以确保始终获得相同的实例。 你会遇到字典越来越大的问题。

C#锁定用于线程安全,并且不能按照您希望的方式用于Web应用程序。

最简单的解决方案是向要锁定的表中添加一列,并在somone锁定时将其写入该列已锁定的数据库。

如果列被锁定以进行编辑,请不要让任何人以编辑模式打开post。

否则,保持锁定条目ID的静态列表,并与允许编辑之前的列表进行比较。

您想确保删除不会发生两次?

 CREATE PROCEDURE RemovePost( @postID int ) AS if exists(select postID from Posts where postID = @postID) BEGIN DELETE FROM Posts where postID = @postID -- Do other stuff END 

这几乎是SQL服务器语法,我不熟悉MyISAM。 但它允许存储过程。 我猜你可以模拟一个类似的程序。

无论如何,这将适用于大多数情况。 它失败的唯一时间是两个主持人几乎完全同时提交,并且exists()函数在DELETE语句执行另一个请求之前传递一个请求。 我很乐意将它用于一个小网站。 你可以更进一步,检查删除实际上删除了一行,然后继续其余的,这将保证所有的primefaces性。

试图在代码中创建锁,对于这个用例,我认为非常不切实际。 你有两个主持人试图删除一个post,一个成功,另一个没有效果,你什么都不会失去。

您应该使用这样的同步对象:

 public class YourForm { private static object syncObject = new object(); public void Moderate() { lock(syncObject) { // do your business } } } 

但是这种方法不应该在Web应用程序场景中使用。