2

私は次のコードを持っています:

private static HashSet<SoloUser> soloUsers = new HashSet<SoloUser>();

    public void findNewPartner(string School, string Major)
    {
        lock (soloUsers)
        {
            SoloUser soloUser = soloUsers.FirstOrDefault(s => (s.School == School) && (s.Major == Major));
            MatchConnection matchConn;
            if (soloUser != null)
            {
                if (soloUser.ConnectionId != Context.ConnectionId)
                {                                                                     
                    soloUsers.Remove(soloUser);
                }

            }
            else
            {   string sessionId = TokenHelper.GenerateSession();                                     

                soloUser = new SoloUser
                {
                    Major = Major,
                    School = School,
                    SessionId = sessionId,
                    ConnectionId = Context.ConnectionId
                };

                soloUsers.Add(soloUser);


            }

        } 

    }

TokenHelper.GenerateToken(soloUser.Session)TokenHelper.GenerateModeratorToken(session);トークンの生成に時間がかかる場合があるため、危険な場合があります。これにより、問題になる可能性のあるその瞬間にすべてのユーザーがロックアウトされますか?すべてをスレッドセーフに保つことができるように、このロジックの回避策はありますか?

編集:とを削除したのはTokenHelper.GenerateToken(soloUser.Session)TokenHelper.GenerateModeratorToken(session)それらがロックの外で発生する可能性があることに気付いたためですが、それぞれSoloUserにというプロパティがSessionIdあり、これはユーザーごとに生成されます。このGenerateSession方法は、少し時間がかかる方法でもあります。各ユーザーはSessionId、コレクションに追加する前に、これらのいずれかを持っている必要があります

4

2 に答える 2

4

ロックを 2 回取得する余裕があり、時折 sessionId が生成されても使用されない場合でも問題ない場合は、GenerateSession をロックから移動できます。

このようなもの:

 public void findNewPartner(string School, string Major)
    {
        SoloUser soloUser = null;

        lock (soloUsers)
        {
            soloUser = soloUsers.FirstOrDefault(s => (s.School == School) && (s.Major == Major));
        }

        string sessionId = null;

        // will we be creating a new soloUser?
        if (soloUser == null)
        { 
            // then we'll need a new session for that new user
            sessionId = TokenHelper.GenerateSession();
        }

        lock (soloUsers)
        {
            soloUser = soloUsers.FirstOrDefault(s => (s.School == School) && (s.Major == Major));
            if (soloUser != null)
            {
                // woops! Guess we don't need that sessionId after all.  Oh well! Carry on...
                if (soloUser.ConnectionId != Context.ConnectionId)
                {                                                                     
                    soloUsers.Remove(soloUser);
                }

            }
            else
            {   
                // use the sessionid computed earlier
                soloUser = new SoloUser
                {
                    Major = Major,
                    School = School,
                    SessionId = sessionId,
                    ConnectionId = Context.ConnectionId
                };

                soloUsers.Add(soloUser);

        }

    } 

これは基本的に、新しい soloUser を構築する必要があるかどうかを確認するためにクイック ロックを行います。必要な場合は、新しいセッションを生成する必要があります。新しいセッションの生成は、ロックの外で行われます。次に、ロックを再取得し、元の一連の操作を実行します。新しい soloUser を構築するときは、ロックの外側で構築された sessionId を使用します。

このパターンは、使用されない sessionId を生成する可能性があります。2 つのスレッドが同じ学校と専攻でこの関数を同時に実行すると、両方のスレッドがセッション ID を生成しますが、新しい soloUser を正常に作成してコレクションに追加できるのは 1 つのスレッドだけです。負けたスレッドは、コレクション内の soloUser を見つけてコレクションから削除します。生成したばかりの sessionId は使用しません。この時点で、両方のスレッドが同じ sessionId を持つ同じ soloUser を参照しています。これが目標のようです。

sessionId にリソース (データベースのエントリなど) が関連付けられているが、sessionId が期限切れになったときにこれらのリソースがクリーンアップされる場合、このような衝突は少し余分なノイズを生成しますが、システム全体には影響しません。

生成された sessionId に、クリーンアップやエージング アウトが必要となるものが何も関連付けられていない場合は、この例の最初のロックを失い、必要かどうかにかかわらず常に sessionId を生成することを検討してください。これはおそらく起こりそうなシナリオではありませんが、トラフィックの多いロックに出入りするのを避けるために、特殊なケースでこの種の「無差別」トリックを使用したことがあります。作成するのに費用がかからず、ロックするのに費用がかかる場合は、放棄して作成し、ロックに注意してください。

GenerateSession のコストが、この余分な回り込みを正当化するのに十分なほど高いことを確認してください。GenerateSession が完了するのにナノ秒かかる場合は、これをすべて行う必要はありません。最初に記述されたとおりにロックしておいてください。GenerateSession に「長い」時間がかかる場合 (1 秒以上? 500 ミリ秒以上? わかりません)、共有リストの他の使用が待機するのを防ぐために、ロックから移動することをお勧めします。

于 2012-07-24T18:05:01.703 に答える
0

最善の解決策は、おそらく を使用することConcurrentBag<T>です。

http://msdn.microsoft.com/en-us/library/dd381779を参照してください。

.NET 4 を使用していると仮定しています。

セットではなく袋ですのでご注意ください。したがって、「重複なし」を自分でコーディングし、スレッドセーフな方法でコーディングする必要があります。

于 2012-07-24T17:35:27.477 に答える