C#読み書きロックを使用してSQLITE同時異常問題を解決

5924 ワード

C#を使用してsqliteにアクセスすると、マルチスレッドによるSQLITEデータベースの破損の問題が発生することがよくあります.
SQLiteはファイルレベルのデータベースで、ロックもファイルレベルです.複数のスレッドは同時に読むことができますが、同時に1つのスレッドしか書くことができません.AndroidはSqliteOpenHelperクラスを提供し、Javaのロックメカニズムを追加して呼び出します.ただし、C#では同様の機能は提供されていません.
著者らは,リードライトロック(Reader WriterLock)を用いて,マルチスレッドセキュリティアクセスの目標を達成した.

using System;
using System.Collections.Generic;
using System.Text;
using System.Data.SQLite;
using System.Threading;
using System.Data;

namespace DataAccess
{

/////////////////
public sealed class SqliteConn
{
private bool m_disposed;
private static Dictionary connPool =
new Dictionary();
private static Dictionary rwl =
new Dictionary();
private static readonly SqliteConn instance = new SqliteConn();
private static string DEFAULT_NAME = "LOCAL";

#region Init
// ,
private SqliteConn()
{
rwl.Add("LOCAL", new ReaderWriterLock());
rwl.Add("DB1", new ReaderWriterLock());
connPool.Add("LOCAL", CreateConn("\\local.db"));
connPool.Add("DB1", CreateConn("\\db1.db"));
Console.WriteLine("INIT FINISHED");
}

private static SQLiteConnection CreateConn(string dbName)
{
SQLiteConnection _conn = new SQLiteConnection();
try
{
string pstr = "pwd";
SQLiteConnectionStringBuilder connstr = new SQLiteConnectionStringBuilder();
connstr.DataSource = Environment.CurrentDirectory + dbName;
_conn.ConnectionString = connstr.ToString();
_conn.SetPassword(pstr);
_conn.Open();
return _conn;
}
catch (Exception exp)
{
Console.WriteLine("===CONN CREATE ERR====\r
{0}", exp.ToString());
return null;
}
}
#endregion

#region Destory
// ,
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}

protected void Dispose(bool disposing)
{
if (!m_disposed)
{
if (disposing)
{
// Release managed resources
Console.WriteLine(" DB ...");
CloseConn();
}
// Release unmanaged resources
m_disposed = true;
}
}

~SqliteConn()
{
Dispose(false);
}

public void CloseConn()
{
foreach (KeyValuePair item in connPool)
{
SQLiteConnection _conn = item.Value;
String _connName = item.Key;
if (_conn != null && _conn.State != ConnectionState.Closed)
{
try
{
_conn.Close();
_conn.Dispose();
_conn = null;
Console.WriteLine("Connection {0} Closed.", _connName);
}
catch (Exception exp)
{
Console.WriteLine(" : DB {0} 。", _connName);
exp.ToString();
}
finally
{
_conn = null;
}
}
}
}
#endregion

#region GetConn
public static SqliteConn GetInstance()
{
return instance;
}

public SQLiteConnection GetConnection(string name)
{
SQLiteConnection _conn = connPool[name];

try
{
if (_conn != null)
{
Console.WriteLine("TRY GET LOCK");
// , , conn
rwl[name].AcquireWriterLock(3000);
Console.WriteLine("LOCK GET");
return _conn;
}
}
catch (Exception exp)
{
Console.WriteLine("===GET CONN ERR====\r
{0}", exp.StackTrace);
}
return null;
}

public void ReleaseConn(string name)
{
try
{
//
Console.WriteLine("RELEASE LOCK");
rwl[name].ReleaseLock();
}
catch (Exception exp)
{
Console.WriteLine("===RELEASE CONN ERR====\r
{0}", exp.StackTrace);
}
}

public SQLiteConnection GetConnection()
{
return GetConnection(DEFAULT_NAME);
}

public void ReleaseConn()
{
ReleaseConn(DEFAULT_NAME);
}
#endregion
}

}
////////////////////////
呼び出されたコードは次のとおりです.

SQLiteConnection conn = null;
try
{
conn = SqliteConn.GetInstance().GetConnection();
//
}
finally
{
SqliteConn.GetInstance().ReleaseConn();
}
接続を申請するたびに、ReleaseConnメソッドを使用して解放する必要があります.そうしないと、他のスレッドは接続できません.
安全のため、著者が書いたこのツールクラスでは、最も厳格な読み書きロック制限(すなわち、書き込み時に読み取り不能)が有効になっています.データの読み取りが頻繁である場合、読者は読み取り専用接続を得る方法を開発して性能を向上させることもできる.
Winxp/Win 7/Win 8/Win 8.1 32/64ビットで試験に合格した.