3

メインの ASP.Net Web サイトの仮想ディレクトリで実行されているスタンドアロンの ASP.Net アプリで SignalR を実行しています。

SignalR ハブの実装では、ConcurrentDictionary<int, UserState>個々の接続全体で軽量のユーザー状態を維持する静的変数があります。その変数は、クライアント側のアクションに基づいて時間の経過とともに追加されます (つまり、新しいユーザーが当社の Web サイトとやり取りを開始すると)。この変数は基本的に、接続全体でいくつかの単純な状態追跡を提供しています。

データの読み込みは比較的軽量である可能性が高く、このメモリ内の追跡で十分であるため、インフラストラクチャの依存関係を追加する必要がある特別な SignalR バックプレーンを追加することは特に望んでいません。

ユーザーが十分な時間 (たとえば 1 時間) 非アクティブであった場合、それらを辞書変数から削除したいと考えています。これを行うプロセスが何であれ、これは一貫して実行されることが保証されるべきです- したがって、ユーザーの行動に依存するのではなく、一定の期間に依存します。

私はこれを行うための良い解決策であると信じているものを持っています:

public class UserStateService : IUserStateService
{
    private static readonly ConcurrentDictionary<int, UserState> recentUsers = new ConcurrentDictionary<int, UserState>();
    private static Timer timer;

    public static void StartCleanup()
    {
        timer = new Timer( CleanupRecentUsers, null, 0, 60000 );
    }

    public static void StopCleanup()
    {
        timer.Dispose();
    }

    private static void CleanupRecentUsers( object state )
    {
        var now = DateTime.UtcNow;

        var oldUsers = recentUsers.Select( p => p.Value ).Where( u => u.LastActionTime.AddHours( 1 ) > now );

        foreach ( var user in oldUsers )
        {
            UserState removedUser;
            recentUsers.TryRemove( user.UserId, out removedUser );
        }
    }

    // other code for adding/updating user state.
}

前述のとおり、これは良い解決策だと思います。ただし、私はスレッド管理にあまり精通していません (ただし、ASP.Net で静的オブジェクトを扱うのは危険であることは承知しています)。

StartCleanup()アプリケーションのライフサイクルの開始時とStopCleanup()終了時にそれぞれ 1 回ずつ呼び出されます。は、IoC コンテナー (構造マップ) を介しUserStateServiceてクラスに提供され、Hub現在、特別なライフサイクル処理でスコープが設定されていません (つまりSingleton、スレッド スコープではなく、単にインスタンスごとのリクエスト)。

私たちはすでに本番アプリで静的並行辞書を使用しており、パフォーマンスの問題の既知のインスタンスなしで正常に動作しています. よくわからないのは、Timerここでループを実行していることです。

したがって、私の質問は、スレッドがブロック/ロックされている (または CPU 使用が一般的に何らかの理由で制御不能になっている) ことに関連して、緩和する必要がある、またはこのアプローチを実行できなくする可能性のある明らかなリスクはありますか?

4

1 に答える 1

3

Timerあなたが提案する方法でa を使用しても特に問題はありません。

ただし、コードにはいくつかの問題があります。

まず、次のものがあります。

var oldUsers = recentUsers
               .Select( p => p.Value )
               .Where( u => u.LastActionTime.AddHours( 1 ) > now );

これにより、最後のアクティビティが過去 1 時間以内に行われたすべてのユーザーが削除されます。したがって、1 分前に見た人は削除されます。その結果、recentUsersほとんどの場合、リストはおそらく空になります。せいぜい、少なくとも 1 時間前に最後に表示されたユーザーが含まれます。

に変更したいと思います<。または、別の方法で考えてみます。

.Where((now - u.LastActionTime) > TimeSpan.FromHours(1));

また、削除対象として選択されたユーザーが実際に削除が行われる前にリクエストを行う可能性があるという競合状態が発生する可能性があるため、リクエストを行ったばかりのユーザーを削除することになります。ただし、その競合状態の時間枠はかなり狭く、おそらく心配する必要はありません。

于 2013-11-11T19:30:47.353 に答える