2

我编写了一个简单的 Mono C# 应用程序,用于使用Mono.Data.Sqlite包中的 Mono 实现写入 SQLite 数据库:

using System;
using Mono.Data.Sqlite;

class MainClass
{
    public static void Main (string[] args)
    {
        using (var dbConnection = new SqliteConnection (@"Data Source=/var/log/gmblog;Version=3;"))
        {
            dbConnection.Open();

            string sql = @"INSERT INTO ""queue"" (""data"") VALUES(""Test"")";
            using (var insertCommand = new SqliteCommand (sql, dbConnection))
            {
                insertCommand.ExecuteNonQuery();
            }
        }
    }
}

这工作正常,直到我从另一个应用程序插入sqlite3并保持该应用程序运行:

sqlite> insert into queue ("data") VALUES("test2");

现在 C# 程序挂起,直到它给出以下错误:

Unhandled Exception: Mono.Data.Sqlite.SqliteException: The database file is locked

sqlite3我从其他实例或从我创建的 C++ 应用程序写入表没有任何问题。

如果我关闭该sqlite3实例,则 C# 应用程序将再次运行。

执行 INSERT 后执行已获得读卡器锁的lsof /var/log/gmblog节目:sqlite3

sqlite3 15578  cup    3ur  REG   8,17    13312 4988505 /var/log/gmblog

在 INSERT 之前它没有这个锁:

sqlite3 15578  cup    3u   REG   8,17    13312 4988505 /var/log/gmblog

但正如我指出的那样,当其他应用程序正在使用数据库时,其他应用程序没有任何问题写入表。

关于我的 C# 代码有什么问题的任何想法?它是 SQLite 的 Mono 实现中的错误吗?

25/11 更新

请注意,这是dbConnection.Open();导致数据库锁定错误的原因,而不是insertCommand.ExecuteNonQuery();. 即以下代码也不起作用:

using System;
using Mono.Data.Sqlite;

class MainClass
{
    public static void Main (string[] args)
    {
        using (var dbConnection = new SqliteConnection (@"Data Source=/var/log/gmblog;Version=3;"))
        {
            dbConnection.Open();
        }
    }
}
4

2 回答 2

4

请参阅SQLiteConnection 文档:“如果 SQLiteConnection 超出范围,则不会关闭。因此,您必须通过调用 Close 显式关闭连接。”。该示例显示连接在 finally 块中关闭。我在您的代码示例中看不到您关闭连接的任何地方。

您还应该能够使用在对象超出范围时自动调用 close 的“使用”块:

using (var dbConnection = new SqliteConnection (@"..."))
{
    dbConnection.Open();

    string sql = @"INSERT INTO ""queue"" (""data"") VALUES(""Test"")";
    using (var insertCommand = new SqliteCommand (sql, dbConnection))
    {
        insertCommand.ExecuteNonQuery();
    }
}

这可以确保所有内容都得到正确和及时的发布。

于 2012-11-23T15:23:17.917 回答
1

我在 sqlite 的多线程网站上找到了类似的讨论。我知道您在那里不是多线程,而是试图获得写锁 - 但讨论仍然有一些有用的提示可能会有所帮助。

然后每个线程继续插入许多记录,假设是 1000。您将遇到的问题如下:一个线程将通过对文件设置锁定来控制数据库。这很好,但是当锁处于活动状态时,其余线程将继续为每次尝试的 INSERT 失败。

Solution

测试我最初没有做的 SQLITE_BUSY。这里有一些伪代码来说明一个解决方案:

  while (continueTrying) {
    retval = sqlite_exec(db, sqlQuery, callback, 0, &msg);
    switch (retval) {
      case SQLITE_BUSY:
        Log("[%s] SQLITE_BUSY: sleeping fow a while...", threadName);
        sleep a bit... (use something like sleep(), for example)
        break;
      case SQLITE_OK:
        continueTrying = NO; // We're done
        break;
      default:
        Log("[%s] Can't execute \"%s\": %s\n", threadName, sqlQuery, msg);
        continueTrying = NO;
        break;
    }
  }

您可能还想尝试连接字符串上的 busy_timeout 参数,如此此处所示。

busy_timeout 参数被实现为对 sqlite(3)_busy_timeout 的调用。默认值为 0,表示如果数据库被锁定,则立即抛出 SqliteBusyException。

于 2012-11-26T11:58:37.270 回答