7

私はFMDBを使用して、正常に機能するデータベースを処理しています。アプリは、いくつかの作業を行っており、データベースにアクセスする必要があるバックグラウンドスレッドを使用します。同時に、メインスレッドは同じデータベースでいくつかのクエリを実行する必要があります。FMDB自体には小さなロックシステムがありますが、クラスにもう1つ追加しました。

すべてのクエリは、私のクラスがデータベースが使用されていないことを示している場合にのみ実行されます。アクションを実行した後、データベースのロックが解除されます。これは、負荷が高すぎない限り、期待どおりに機能します。メインスレッドで実行されているスレッドで大量のデータにアクセスすると、EXC_BAD_ACCESSエラーが発生します。

これが見た目です:

- (BOOL)isDatabaseLocked {
    return isDatabaseLocked;
}

- (Pile *)lockDatabase {
    isDatabaseLocked = YES;
    return self;        
}

- (FMDatabase *)lockedDatabase {
    @synchronized(self) {
        while ([self isDatabaseLocked]) {
            usleep(20);
            //NSLog(@"Waiting until database gets unlocked...");
        }
        isDatabaseLocked = YES;
        return self.database;       
    }
}

- (Pile *)unlockDatabase {
    isDatabaseLocked = NO;
    return self;            
}

デバッガーは、エラーが[FMResultSet next]行で発生すると言います

rc = sqlite3_step(statement.statement);

すべての保持カウントを再確認しましたが、現時点ではすべてのオブジェクトが存在します。繰り返しになりますが、これは、バックグラウンドスレッドの実行中にメインスレッドが多くのクエリを開始した場合にのみ発生します(それ自体が常に重い負荷を生成します)。エラーは常にメインスレッドによって生成され、バックグラウンドスレッドによって生成されることはありません。

私の最後のアイデアは、両方のスレッドがlockedDatabaseを同時に実行して、データベースオブジェクトを取得できるようにすることです。そのため、「@ synchronized(self)」を介してミューテックスロックを追加しました。しかし、これは役に立ちませんでした。

誰か手がかりがありますか?

4

4 に答える 4

6

SQLiteは、はるかに単純なシリアル化を提供します。sqlite_config()オプションSQLITE_CONFIG_SERIALIZEDを設定するだけで、これらの種類の頭痛の種のほとんどをおそらく回避できます。長い間スレッドの問題と戦った後、私はこれを困難な方法で発見しました。

使い方は次のとおりです。FMDatabaseのinitメソッドに入れることができます...

    if (sqlite3_config(SQLITE_CONFIG_SERIALIZED) == SQLITE_ERROR) {
        NSLog(@"couldn't set serialized mode");
    }

詳細については、スレッドセーフシリアル化モードに関するSQLiteドキュメントを参照してください。

于 2012-07-29T17:38:37.107 に答える
2

関数unlockDatabaseとlockDatabase、およびisDatabaseLockedの周りに同期ラッパーを追加する必要があります。変数の格納または取得がアトミックであることが常に保証されるわけではありません。もちろん、そうする場合は、同期ブロックの外にスリープを移動する必要があります。そうしないと、デッドロックが発生します。これは本質的にスピンロックであり、最も効率的な方法ではありません。

- (FMDatabase *)lockedDatabase {
    do
    {
        @synchronized(self) {
            if (![self isDatabaseLocked]) {
                isDatabaseLocked = YES;
                return self.database; 
            }
        }
        usleep(20);      
    }while(true); // continue until we get a lock
}

ロック解除データベースを呼び出した後、FMDatabaseオブジェクトを使用しないようにしていますか?ハンドルパターンを検討することをお勧めします。FMDatabaseオブジェクトをラップするオブジェクトを作成し、それが存在する限り、データベースのロックを保持します。initでロックを要求し、deallocでそのロックを解放できます。そうすれば、クライアントコードは、さまざまなロック/ロック解除関数の呼び出しについて心配する必要がなくなり、誤って失敗することもありません。@synchronizedブロックの代わりにNSMutexを使用してみてください。http://developer.apple.com/mac/library/documentation/Cocoa/Conceptual/Multithreading/ThreadSafety/ThreadSafety.html#//apple_ref/doc/uid/10000057i-CH8を参照してください。 -SW16

于 2010-06-29T21:48:23.230 に答える
0

FMDatabaseQueueも試してみてください-私はこのような状況のために特別に作成しました。試したことはありませんが、iOS4で動作すると確信しています。

于 2012-05-19T01:18:21.550 に答える
0

私はこの問題を抱えていて、プリペアドステートメントのキャッシュをオンにするだけで問題を解決することができました。

FMDatabase *myDatabase = [FMDatabase databaseWithPath: pathToDatabase];
myDatabase.shouldCacheStatements = YES;
于 2015-04-17T19:00:21.463 に答える