`
yacki
  • 浏览: 47048 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类

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位下测试通过。
2
0
分享到:
评论

相关推荐

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

    本文实例讲述了C#解决SQlite并发异常问题的方法。分享给大家供大家参考,具体如下: 使用C#访问sqlite时,常会遇到多线程并发导致SQLITE数据库损坏的问题。 SQLite是文件级别的数据库,其锁也是文件级别的:多个线程...

    C#使用读写锁三行代码简单解决多线程并发的问题

    而使用了读写锁后,所有日志都能够正确地写入文件,从而解决了并发问题。 总结来说,C#的ReaderWriterLockSlim类是解决多线程并发写入文件问题的有效工具。通过使用这个类,我们可以确保在多个线程访问同一文件时,...

    C#多线程读写sqlite

    4. **同步锁**:在描述中提到的“同步锁”是解决并发问题的关键。C#中的锁机制,如`Monitor`类、`Mutex`类和`Semaphore`类,可用于控制对公共资源的访问。例如,使用`lock`语句可以创建一个临界区,只允许一个线程在...

    vs2019 C# 对SQLite数据库的增删改查的代码实例

    这是一个用C#实现的读写sqlite数据库的例子,希望能对正学习这一块的朋友有帮助。 需要使用System.Data.SQLite库 大至步骤如下: 在WinForms项目中添加一个数据库连接。你可以使用SQLiteConnection类来建立数据库...

    解决sqlite死锁示例异常database is locked示例

    总结来说,SQLite的死锁问题主要源于资源竞争和事务顺序,通过理解死锁产生的原因,结合异常处理、事务设计优化、日志监控等手段,可以有效地预防和解决"database is locked"异常。在实际开发中,应当重视数据库的...

    sqlite死锁datebaselock解决方案

    在多线程环境下,由于并发操作不当,可能会出现“database is locked”(数据库被锁定)的错误,这通常涉及到SQLite的锁机制和事务处理。本文将深入探讨这个问题,并提供具体的解决方案。 一、SQLite锁机制 SQLite...

    SqliteHelper.zip

    采用ReaderWriterLockSlim锁,实现读写锁分离,多线程操作sqlite...参考 让C#轻松实现读写锁分离--封装ReaderWriterLockSlim C#解决SQlite并发异常问题的方法(使用读写锁)2篇文章,已经封装sqlite常用增删改查函数。

    C#工具包 dataGridView sqlite 多线程

    在C#中,可以使用`System.IO`命名空间下的类来检测USB设备的插入和移除,并进行文件的读写操作。同时,需要考虑错误处理,如设备未准备好或文件正在使用等情况。 5. **SQLite数据库操作**: SQLite是一个轻量级的...

    C#操作SQLite的示例代码

    在.NET开发环境中,C#是一种常用的编程语言,而SQLite则是一种轻量级、无服务器、自包含的数据库引擎。这个示例代码集主要是为了帮助初学者理解如何在C#项目中与SQLite数据库进行交互。下面将详细介绍C#操作SQLite的...

    sqlite-amalgamation-3070601.zip_c# sqlite_sqlite

    3. C#接口:在C#中使用SQLite,通常会借助于ADO.NET或其他第三方库,如System.Data.SQLite或SQLite.NET。这些库提供了SQLiteConnection、SQLiteCommand、SQLiteDataReader等类,便于执行SQL命令和处理结果集。 4. ...

    sqlite数据库 大数据量处理demo

    9. **并发控制**:SQLite支持多线程操作,但并发访问时需注意锁的使用,以防止数据冲突和死锁。 10. **硬件配置**:提升存储设备的速度,如使用SSD,可以显著提高SQLite处理大数据时的读写性能。 在"BarCodeTest...

    System.Data.SQLite不同操作系统

    在实际开发中,使用System.Data.SQLite时,开发者需要了解如何正确安装和引用DLL文件,如何连接到数据库,编写SQL语句,以及处理事务、异常和并发问题。此外,还要熟悉SQLite的数据类型、约束和索引等基本概念。对于...

    sqlite资源包

    6. **并发性**:SQLite支持多种并发控制模式,可以在多用户环境中安全地读写数据。 7. **跨平台兼容性**:SQLite与多种编程语言(如C#、Java、Python、PHP等)兼容,通过提供各种语言的API,使得在不同平台上使用...

    sqlite3 64位开发sdk

    开发者在自己的应用程序中调用这些API来实现对SQLite数据库的读写操作。 2. **sqlite3.h**: 这是SQLite3的头文件,包含了所有可供开发者使用的API函数声明和数据结构定义。在C或C++项目中,将此头文件包含在源代码...

    sqlite测试程序

    测试程序应涵盖并发读写场景,确保在多用户环境下的数据一致性。 8. 数据备份与恢复:SQLite支持导出和导入数据,这对于数据备份和迁移非常方便。测试这部分功能可以确保数据在不同环境间迁移时的完整性和一致性。 ...

    SQLite的.dll和.so文件及相关问题文档

    本文将详细讨论SQLite的.dll和.so文件,以及在使用过程中可能遇到的问题和解决方案。 首先,`.dll`(动态链接库)文件是Windows平台上的共享库,它包含了可被多个程序同时调用的函数和资源。在Unity3D的Windows平台...

    The Definitive Guide to SQLite

    对于并发访问和多用户场景,SQLite提供了多线程支持和读写锁机制。读者将了解到如何在多线程环境中正确地使用SQLite,以及如何设置适当的隔离级别以防止数据竞争。 除此之外,本书还会涉及数据库备份、恢复、安全性...

    SQLite Express

    在实际应用中,开发者可以使用SQLite Express来快速构建原型,或者作为小型应用程序的数据存储解决方案。只需按照`install.txt`的指示进行操作,将必要的库文件添加到项目中,并根据需要调用API进行数据库操作。同时...

Global site tag (gtag.js) - Google Analytics