欢迎来到Doc100.Net免费学习资源知识分享平台!
您的位置:首页 > 程序异常 >

c#使用读写锁解决sqlite并发错误有关问题

更新时间: 2014-01-05 02:06:47 责任编辑: Author_N1

 

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位下测试通过。
上一篇:上一篇
下一篇:下一篇

 

随机推荐程序问答结果

 

 

如对文章有任何疑问请提交到问题反馈,或者您对内容不满意,请您反馈给我们DOC100.NET论坛发贴求解。
DOC100.NET资源网,机器学习分类整理更新日期::2014-01-05 02:06:47
如需转载,请注明文章出处和来源网址:http://www.doc100.net/bugs/t/5754/
本文WWW.DOC100.NET DOC100.NET版权所有。