以下代码不安全(或者至少我认为是):
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,问题就会再次发生。
答案 0 :(得分:2)
你可以有一个通用的包装类,它接受一个IDisposable参数,以及相同的字符串参数,它可以锁定并可以使用它
例如(未经测试的代码):
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");