问题描述
我正在将Microsoft.Data.sqlite.Core
与sqlitePCLRaw.lib.e_sqlcipher
一起使用来访问加密的sqlite数据库。我需要解决一些挑战:
- 我必须确保
Microsoft.Data.sqlite.sqliteConnection
的同一实例不在多个线程之间共享(sqlite连接不是线程安全的); - 打开sqlCipher加密数据库非常昂贵,因此我必须将发生的次数限制为最少。
我想出了解决方案,但是我不确定该解决方案的可靠性如何,所以我希望有人可以对此提出一些漏洞。
解决方法
这是我的解决方案,请让我知道我是否搞砸了:
public class SqliteConnectionPool
{
private readonly object lockObject_ = new object();
private readonly string connectionString_;
private readonly Dictionary<Thread,SqliteConnection> pool_;
public SqliteConnectionPool(string connectionString)
{
new SqliteConnectionStringBuilder
{
ConnectionString = connectionString // throws if connection string is invalid
};
connectionString_ = connectionString;
pool_ = new Dictionary<Thread,SqliteConnection>();
}
public SqliteConnection GetConnection()
{
lock (lockObject_)
{
Thread currentThread = Thread.CurrentThread;
// If this thread owns a connection,just retrieve it.
if (pool_.ContainsKey(currentThread))
{
return pool_[currentThread];
}
// Looking for a thread that doesn't need its connection anymore.
(Thread inactiveThread,SqliteConnection availableConnection) = pool_.Where(p => p.Key.ThreadState != ThreadState.Running)
.Select(p => (p.Key,p.Value))
.FirstOrDefault();
// If all existing connections are being used,create a new one.
if (availableConnection is null)
{
var connection = new SqliteConnection(connectionString_);
pool_[currentThread] = connection;
return connection;
}
// Otherwise,just use the existing free connection.
pool_.Remove(inactiveThread);
pool_[currentThread] = availableConnection;
return availableConnection;
}
}
}
,
我面临着与您相同的认识,并且必须做类似的事情。我认为您的代码没有问题。
我将尝试使用 ConcurrentDictionary 进行一些优化。它应该能让我避免在更新时锁定读者。
如果您继续使用 Dictionary,您可能希望将 ContainsKey() 调用更改为 TryGetValue(),因为文档表明如果使用不存在的键可能会更有效(我们经常会看到新线程?)
如果其他人开车经过,这是我对这个问题的背景研究: Microsoft.Data.Sqlite 派生自 ADO.NET 对象,例如根据设计,DbConnection 不是线程安全的。 ADO.NET 的设计者在高性能的祭坛上牺牲了线程安全。出于这个原因,任何使用从 ADO.NET 派生的任何代码的任何代码最终都需要进入这个兔子洞,以确保不会发生真正奇怪的事情。
至于 Microsoft.Data.Sqlite,我的问题是 SqliteCommand.Dispose,它在内部遇到空值并崩溃。这是框架在不同线程上有大量并行调用的时候。
您可能还注意到 sqlite 本身具有多线程设置,并相信这个兔子洞适合您。不幸的是,摆弄这些设置,尽管它们可能有益,但不会改变 ADO.NET 对象设计,因此只要您使用 Microsoft.Data.Sqlite 访问 sqlite,问题就会一直存在。
欢迎发布您的代码!希望它能够成功地投入生产:-)
/尼克
发帖后补充: 保存对 Thread 和 SqliteConnection 的引用的 Dictionary 将对资源产生影响,因为只要引用存在,它就会停止垃圾收集器回收它们的资源。 SqliteConnections 也是如此。 可能不是一个真正的问题,但人们可能还想考虑某种清理算法,在一段时间后删除陈旧的 Thread 引用。如果将 SqliteConnections 移动并单独存储,仍然可以重用。
/尼克