【问题标题】:combining using statements and locks结合使用语句和锁
【发布时间】:2015-08-26 17:39:44
【问题描述】:

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

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 包装在锁中。我的问题是,有没有办法将 using 和 lock 结合起来让它不那么笨重?

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

【问题讨论】:

  • 我认为您应该在using(var connection = new...){ 中编写一个try-catch,而不是锁定。尝试创建一个表,如果它抛出异常,您知道该表已经存在。更好的是使用 If(tableExists) 之类的东西向表中添加行,否则创建它。
  • 你确定数据库会抛出不同的“tableName”吗?似乎很容易序列化。
  • 它确实对我产生了影响。我不知道为什么——它被捕获在一个不再明显的通用级别。
  • 不是表存在的问题。异常说“数据库被锁定”,这一定意味着另一个线程正在访问它。考虑到我当时的所作所为,那会发生是有道理的。
  • 不确定这里的锁定是否足够。 CreateTable 需要独占访问,同时从另一个线程查询/更新 Db 是否有效?

标签: c# multithreading using locks


【解决方案1】:

你可以有一个通用的包装类,它接受一个 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");

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-04-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多