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

作者:yacki 时间:2023-08-03 04:06:02 

本文实例讲述了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#,SQlite
0
投稿

猜你喜欢

  • Android发送xml数据给服务器的方法

    2021-12-19 13:06:44
  • C#快速排序算法实例分析

    2023-06-17 00:30:00
  • Android Apk反编译及加密教程

    2022-01-13 01:01:25
  • JavaBean和Map转换封装类的方法

    2023-04-18 06:50:52
  • java中Servlet程序下载文件实例详解

    2022-04-28 18:26:10
  • Android 屏幕双击事件的捕获简单示例

    2022-08-30 11:02:55
  • Android打包篇:Android Studio将代码打包成jar包教程

    2022-08-14 06:22:20
  • C#多态的三种实现方式(小结)

    2022-12-29 18:10:57
  • Android Vitamio和ExoPlayer两种播放器优劣分析

    2023-02-25 13:21:51
  • c#入门之实现简易存款利息计算器示例

    2023-12-15 06:54:36
  • c# 通过内存映射实现文件共享内存的示例代码

    2023-03-02 12:15:35
  • 极简的Resty服务端和客户端RESTful框架

    2022-01-19 19:51:00
  • C# FTP操作类分享

    2022-05-11 22:03:49
  • Groovy的规则脚本引擎实例解读

    2023-07-11 21:24:04
  • javascript最新2020经典面试题

    2023-05-15 09:04:43
  • C# PictureBox图片控件实现图片交换

    2022-12-05 17:09:10
  • 解决使用mybatis-plus时,生成的SQL大写变小写加下划线问题

    2022-10-20 09:24:47
  • C#/Java连接sqlite与使用技巧

    2021-07-10 08:34:05
  • Android优化之电量优化的实现

    2023-08-20 07:57:25
  • Android WebView基础应用详解

    2023-09-30 07:36:46
  • asp之家 软件编程 m.aspxhome.com