C# 使用语句和锁组合
以下代码是不安全的(或者至少我认为是): 问题是,如果另一个线程试图在同一路径的不同连接上创建表,数据库将被锁定,因此将引发异常。为了防止出现这种情况,我可以采取以下措施:C# 使用语句和锁组合,c#,multithreading,using,locks,C#,Multithreading,Using,Locks,以下代码是不安全的(或者至少我认为是): 问题是,如果另一个线程试图在同一路径的不同连接上创建表,数据库将被锁定,因此将引发异常。为了防止出现这种情况,我可以采取以下措施: object lockObject = myLockHelper.GetUniqueObjectForLocking("path"); //does what it claims to do; implementation not shown lock (lockObject) { using (SQLiteConnec
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,问题将再次出现。您可以有一个通用包装类,它接受IDisposable参数和相同的字符串参数,锁在哪个位置,在哪个位置可以使用 e、 g.(未测试代码): 另一种方法是一直使用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);
}
}
}
我认为您应该使用(var connection=new…{)在
中编写一个try catch,而不是锁定。尝试创建一个表,如果它抛出异常,您就知道该表已经存在。更好的方法是使用类似if(tableExists)的方法
将行添加到表中,否则创建它。您确定数据库会为不同的“tableName”抛出吗?这似乎是很容易序列化的事情。它确实对我产生了影响。我不确定为什么——它是在一个通用级别捕获的,不再明显。这不是表存在的问题。异常说“数据库已锁定”,这一定意味着另一个线程正在访问它。考虑到我当时正在做的事情,这是有意义的。不确定这里的锁定是否足够。CreateTable需要独占访问,同时从另一个线程查询/更新数据库是否有效?
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);
}
using(var lck = new SQLiteConnection("path").StartLock("path"))
MyTableCreationHelper.CreateTable(lck.Object, "tableName"));
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");