2

Mono.Data.Sqliteパッケージに含まれているMono実装を使用してSQLiteデータベースに書き込むための単純なMonoC#アプリケーションを作成しました。

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++アプリケーションの他のインスタンスまたは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 のドキュメントを参照してください。この例は、finally ブロックで閉じられている接続を示しています。接続を閉じるコード サンプルのどこにも表示されません。

また、オブジェクトがスコープ外になったときに自動的に close を呼び出す「using」ブロックを使用できる必要があります。

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のWebサイトで同様の議論を見つけました。マルチスレッドではなく、書き込みロックを取得しようとしていることは理解していますが、それでも議論には役立つヒントがいくつかあります。

次に、各スレッドは多数のレコード (1000 としましょう) の挿入に進みます。発生する問題は次のとおりです。ファイルにロックを設定することにより、1 つのスレッドがデータベースを制御します。これは問題ありませんが、ロックがアクティブな間、残りのスレッドは 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;
    }
  }

hereおよびhereに示すように、接続文字列で busy_timeout パラメータを試すこともできます。

busy_timeout パラメータは、sqlite(3)_busy_timeout への呼び出しとして実装されています。デフォルト値は 0 です。これは、データベースがロックされている場合に SqliteBusyException をすぐにスローすることを意味します。

于 2012-11-26T11:58:37.270 に答える