结合使用语句和锁

本文关键字:语句 结合 | 更新日期: 2023-09-27 18:10:07

下面的代码是不安全的(至少我认为是这样):

using (SQLiteConnection connection = new SQLiteConnection("path")) {
  MyTableCreationHelper.CreateTable(connection, "tableName"));
}

问题是,如果另一个线程试图在到同一路径的不同连接上创建表,数据库将被锁定,因此将抛出异常。为了防止这种情况发生,我可以这样做:

object lockObject = myLockHelper.GetUniqueObjectForLocking("path"); //does what it claims to do; implementation not shown
lock (lockObject) {
  using (SQLiteConnection connection = new SQLiteConnection("path")) {
    MyTableCreationHelper.CreateTable(connection, "tableName"));
  }
}

现在代码是安全的,但也更笨拙,因为我每次都必须将using包装在锁中。我的问题是,有没有办法把使用和锁结合起来,让它不那么笨重?

理想情况下,它将以一种不依赖于我们的内部操作涉及SQLiteConnection的事实的方式完成。换句话说,为SQLiteConnection编写一个锁定包装器不是一个理想的解决方案,因为如果下次我的锁不涉及SQLite,那么问题将再次出现。

结合使用语句和锁

您可以有一个通用的包装器类,它接受一个IDisposable参数,以及相同的字符串参数,该参数执行锁定并在其上使用using

。(未测试的代码):

public class LockWrapper<T>:IDisposable
    where T:IDisposable
{
    T obj;
    object lockObject ;
    public LockWrapper(T obj, string Name)
         :this(()=>obj, Name)
    {
    }   

    public LockWrapper(Func<T> objcreator, string Name)
    {
        lockObject = myLockHelper.GetUniqueObjectForLocking("path");
        Monitor.Enter(lockObject);
        this.obj = objcreator();
    }
    public T Object{get{return obj;}}
    public void Dispose()
    {
        try
        {
            obj.Dispose();
        }
        finally
        {
            Monitor.Exit(lockObject);
        }
    }
}
//helper inside a static class
public static LockWrapper<T> StartLock(this T obj, string LockName)
    where T:IDisposable
{
    return new LockWrapper<T>(obj, LockName);
}

这是直接输入到SO中,所以我甚至不知道我是否犯了语法错误,但思想是一样的,包装器对象负责锁定。

调用应该是这样的:

using(var lck = new SQLiteConnection("path").StartLock("path"))
    MyTableCreationHelper.CreateTable(lck.Object, "tableName"));

另一种方法是一直使用lambda并使用像

这样的东西
 public static void RunLocked<T>(Func<T> objCreator, Action<T> run, string LockName)
    where T:IDisposable
 {
    lock(getlockobject(LockName))
    {
        using(var obj = objCreator())
        {
            run(obj);
        }
    }
 }

但是调用就不那么直观了:

    RunLocked(()=> new SQLiteConnection("path"),
            connection => MyTableCreationHelper.CreateTable(connection , "tableName"), 
             "path");