【问题标题】:Lock only if value is same?仅当值相同时才锁定?
【发布时间】:2013-09-25 15:21:38
【问题描述】:

如果我有一个在我的网络服务中编辑数据库的函数,如果他们尝试编辑同一行,我只希望一个线程一次执行。

void EditCheck(long checkid)
    {

        if (isCheckCosed)
            throw new Exception("check is already closed");

        //do stuff

        //after i edit my check i want to close it.
        CloseCheck();
    }

我知道我可以锁定整个功能,但是我失去了性能,因为几乎从来没有不同的线程会尝试编辑相同的检查。

有没有办法只锁定具有相同 checkid 的其他线程?

更新

我使用OleDbConnectionMySqlConnection

OleDbCommand oleDbCommand = AccessTrans != null ?
new OleDbCommand(sql, AccessConn, AccessTrans) : new OleDbCommand(sql, AccessConn); 
oleDbCommand.ExecuteNonQuery();

MySqlCommand 的功能相同

然后我就使用普通的 INSERT 和 UPDATE sql 命令。 并且检查交易是否存在。因此,如果您想在上层函数中进行交易,这个函数就可以工作。

为了从数据库中读取,我将填写DataTable

OleDbCommand oleDbCommand = AccessTrans != null ? new OleDbCommand(sql, AccessConn, AccessTrans) : new OleDbCommand(sql, AccessConn);
OleDbDataAdapter dAdapter = new OleDbDataAdapter();
dAdapter.SelectCommand = oleDbCommand;
dAdapter.Fill(dTable);
return dTable;

【问题讨论】:

  • 不应该在数据库端处理吗?您是否真的有问题,或者这只是基于假设?
  • 数据访问是怎么写的?如果您使用 EF,这可能会有所帮助:blogs.msdn.com/b/alexj/archive/2009/05/20/…
  • 你能展示你是如何访问数据库的吗,许多数据访问方法都内置了很好的工具来处理这个问题(任何发生在客户端的解决方案只有在只有一个客户端连接到数据库。如果您的程序的两个副本都为相同的checkid 调用EditCheck,则当前答案将不起作用)
  • @ScottChamberlain 更新了我的问题以包含数据库代码。

标签: c# multithreading


【解决方案1】:

您可以使用ConcurrentDictionary 将每个 id 映射到您可以锁定的对象:

public class Foo
{
    private ConcurrentDictionary<long, object> dictionary = 
        new ConcurrentDictionary<long, object>();

    private void EditCheck(long checkid)
    {
        var key = dictionary.GetOrAdd(checkid, new object());
        lock (key)
        {
            //Do stuff with key
        }
    }
}

【讨论】:

  • 不错。对this answer的改进
  • 感谢您的回答,我将阅读 ConcurrentDictionary 并对此进行测试。但似乎正是我所需要的。
  • 如果您将有很多线程同时在 checkid 上执行只读操作,您可能还想考虑使用 ReadWriterLock,这将让多个线程都在同一个checkid 上执行读取操作,但是一旦有人想要写入它就会锁定所有并发读取器。
  • 抱歉,如果我接受答案的速度很慢,我还没有能够正确测试这个,但是我们今天将测试我们的客户端计算机是否能够拥有 .net 4.0。因为它们基于 xp 但未在支持的操作系统 xD 中列出
  • 完成后我们不应该清理一下吗?否则它将是一本无限增长的字典
【解决方案2】:

由于您想提高性能,您应该尽可能在最近的时刻进行锁定。当您使用数据库时,您可以使用数据库锁定。你有两个选择:

  1. 使用Select for update,看Proper way to SQL select and update
  2. 使用乐观锁定,这是恕我直言的最佳方法,http://en.wikipedia.org/wiki/Optimistic_concurrency_controlhttp://docs.jboss.org/hibernate/orm/4.0/devguide/en-US/html/ch05.html

请注意,这两种方法都允许您使用数据库资源而不是编程资源。这比提议的字典更好,为什么?

一旦你要复制数据,DB 中的那些在运行时字典中被复制,你必须同步它们,所以如果有人更新数据库并添加新的id,你需要立即更新字典。

当然,它对于小型维护(例如存储库)类是可行的,但是一旦您的项目发展壮大,您的一些同事可能会忘记此信息,您将开始发现此类错误。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-02-18
    • 2019-07-19
    • 1970-01-01
    • 1970-01-01
    • 2021-08-07
    • 2018-12-29
    • 2015-03-29
    相关资源
    最近更新 更多