1

データベースの作業を行う次のコードがあります。

[WebMethod]
public void FastBulkAdd(int addmax){
Users[] uploaders = db.Users.Take(addmax).ToArray();

Parallel.ForEach(uploaders, item =>
{
    Account account;
    lock (this)
    {
        account = item.Account;
    }
}

すべてのユーザーが 1 つのアカウントを持ち、外部キーを介して DB の別のテーブルで参照されます (各ユーザーが正確に 1 つのアカウントを持っていると確信しています)。マルチスレッドのデータベース接続ではエラーが発生するため、そのコードをロックする必要があります。この addmax を 1 に設定して (1 つのスレッドの実行を許可する) 実行すると、問題なく動作しますが、addmax が 1 よりも大きく、複数のスレッドが実行されると、アカウントは常にnull になり、後で例外が生成されます。ロックがスキップされているようです。

更新:アカウントが常に null になるとは確信していなかったので、次のことを行いました。

int tries = 0;
while (account == null && tries < 100)
{
    lock (this)
    {
        account = item.Account;
    }
    tries++;
}

そして、それはうまくいきました。あまりきちんとした解決策ではありません。今後この設計上の問題を回避できるように、問題の原因を知りたいです。

4

3 に答える 3

2

item.AccountDBルックアップですよね?アップローダのすべてのアカウントを一度に選択する一括選択に置き換えていただけますか? そうすれば、選択するデータベースに 1 つのヒットを作成し、後で一括更新するためにデータベースに 1 つのヒットを作成するだけで済みます。また、同期されたデータベース アクセスを気にする必要はありません (いずれにせよ、追加のヒットごとに多くの時間がかかります)。

于 2013-02-09T07:49:37.837 に答える
0

ロックする代わりに をthis作成しprivate static objectてロックします。このスレッドを参照してください。

また、item.Account が null でないことを確認する必要があります。もう 1 つの問題は、 をロックしてAccountいても、このコードの後半で使用しているように見えることです。ロックしていても、ロックされていないため、後でデータベースに保存するセクションで変更される可能性があります。次のサンプルを参照してください。

lock (this)
{
    account = item.Account;
}
DoSomeDatabaseOperation(account); // the account may change here when another thread is also operating.

また、並列操作をデバッグすることもできます。この msdn ページを参照してください。

于 2013-02-09T07:35:34.033 に答える
0

[MethodImpl(MethodImplOptions.Synchronized)] を使用できます。たとえば

  [MethodImpl(MethodImplOptions.Synchronized)]
    [WebMethod]
    public void FastBulkAdd(int addmax)
    {
    }

詳細については、以下のリンクを参照してください。 http://msdn.microsoft.com/en-us/library/system.runtime.compilerservices.methodimploptions.aspx

于 2013-02-09T07:59:38.083 に答える