欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

C#解决SQlite并发异常问题的方法(使用读写锁)

程序员文章站 2022-05-18 12:59:21
本文实例讲述了c#解决sqlite并发异常问题的方法。分享给大家供大家参考,具体如下: 使用c#访问sqlite时,常会遇到多线程并发导致sqlite数据库损坏的问题。...

本文实例讲述了c#解决sqlite并发异常问题的方法。分享给大家供大家参考,具体如下:

使用c#访问sqlite时,常会遇到多线程并发导致sqlite数据库损坏的问题

sqlite是文件级别的数据库,其锁也是文件级别的:多个线程可以同时读,但是同时只能有一个线程写。android提供了sqliteopenhelper类,加入java的锁机制以便调用。但在c#中未提供类似功能。

作者利用读写锁(readerwriterlock),达到了多线程安全访问的目标。

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<string, sqliteconnection> connpool =
    new dictionary<string, sqliteconnection>();
  private static dictionary<string, readerwriterlock> rwl =
    new dictionary<string, readerwriterlock>();
  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\n{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<string, sqliteconnection> 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\n{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\n{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/win7/win8/win8.1 32/64位下测试通过。

更多关于c#相关内容感兴趣的读者可查看本站专题:《c#程序设计之线程使用技巧总结》、《c#操作excel技巧总结》、《c#中xml文件操作技巧汇总》、《c#常见控件用法教程》、《winform控件用法总结》、《c#数据结构与算法教程》、《c#数组操作技巧总结》及《c#面向对象程序设计入门教程

希望本文所述对大家c#程序设计有所帮助。