结合使用语句和锁

时间:2015-08-26 17:39:44

标签: c# multithreading using locks

以下代码不安全(或者至少我认为是):

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"));
  }
}

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

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

1 个答案:

答案 0 :(得分:2)

你可以有一个通用的包装类,它接受一个I​​Disposable参数,以及相同的字符串参数,它可以锁定并可以使用它

例如(未经测试的代码):

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");