2

私が書いたいくつかのコードは次のとおりです。

これは、メソッド呼び出しの期間を非同期的に記録する目的で PostSharp アスペクトをメソッドに適用する方法を示しています。これにより、ロギング プロセスが遅い場合、アスペクトで修飾されたメソッドの呼び出し元にはこのパフォーマンス ペナルティが見られません。 .

MyFirstMethod が完了し、ロギング メソッドが別のスレッドでオフに設定され、MySecondMethod が並行して実行されているため、機能しているようです。これは、非常にトラフィックの多い Web アプリケーション (高度にマルチスレッド化された環境) 内のメソッドを同様のインストルメンテーションで装飾するというものです。

そうすることの落とし穴は何ですか?(たとえば、いつでも許可されるスレッド数の制限に達することを懸念しています)。

using System;
using System.Threading.Tasks;
using NUnit.Framework;
using PostSharp.Aspects;

namespace Test
{
    [TestFixture]
    public class TestClass
    {        
        [Test]
        public void MyTest()
        {            
            MyFirstMethod();
            MySecondMethod();
        }

        [PerformanceInstrument]
        private void MyFirstMethod()
        {
            //do nothing
        }

        private void MySecondMethod()
        {
            for (int x = 0; x < 9999999; x++);
        }
    }

    [Serializable]
    public class PerformanceInstrument : MethodInterceptionAspect
    {                    
        public override void OnInvoke(MethodInterceptionArgs args)
        {            
            var startDtg = DateTime.Now;
            args.Proceed();
            var duration = DateTime.Now - startDtg;
            Task.Factory.StartNew(() => MyLogger.MyLoggingMethod(duration)); //invoke the logging method asynchronously
        }        
    }

    public static class MyLogger
    {
        public static void MyLoggingMethod(TimeSpan duration)
        {
            for (int x = 0; x < 9999999; x++);
            Console.WriteLine(duration);
        }
    }
}
4

2 に答える 2

2

ASP.NETエンジンとタスク並列ライブラリはどちらも.NETスレッドプールでタスクをスケジュールしているため、アプローチによって意図しない結果が生じる可能性があります。各Webリクエストは、スレッドプールのスレッドによって処理されます。ロギングを処理するタスクをスケジュールしている場合は、Webリクエストの処理に使用できなくなったスレッドプール上のタスクを使用することになります。これにより、スループットが低下する可能性があります。

TPLチームはこれについてここでブログを書きました。

http://blogs.msdn.com/b/pfxteam/archive/2010/02/08/9960003.aspx

プロデューサー/コンシューマーパターンは、MethodInterceptionAspectが(Benによって提案されたように)グローバルキューにエントリを追加するだけで、すべてのエントリを処理する単一の(長時間実行される)タスクを持つことを意味します。したがって、インターキャプションメソッドは次のようになります。

ConcurrentQueue<TimeSpan> _queue;

public override void OnInvoke(MethodInterceptionArgs args)
{
    var startDtg = DateTime.Now;
    args.Proceed();
    var duration = DateTime.Now - startDtg;
    Task.Factory.StartNew(() => _queue.Add(duration)); 
}

他の場所でキューを処理します。

foreach (var d in _queue.GetConsumingEnumerable())
{
    Console.WriteLine(d);
}

次の投稿は、Parallel.Forループによって作成された複数のタスクがBlockingCollectionに画像を追加し、単一のタスクが画像を処理する同様の実装を示しています。

並列タスクライブラリWaitAnyDesign

これがどの程度うまく機能するかは、リクエスト処理の長さ、リクエストごとに処理するログエントリの数、サーバー全体の負荷などによって異なります。注意しなければならないことの1つは、全体として次のことができる必要があるということです。追加されるよりも早くキューからリクエストを削除します。

独自のパフォーマンスカウンターを作成し、パフォーマンスカウンターインフラストラクチャに手間のかかる作業を処理させるアプローチについて考えたことはありますか?これにより、このレコーディングインフラストラクチャのいずれかを実装する必要がなくなります。

于 2010-08-03T00:24:36.717 に答える
2

ここで唯一考えられる欠点は、タスクを管理するオーバーヘッドです。これはおそらく取るに足らないものであると確信していますが、確実にするために TPL のことを深く掘り下げていません。

私が大規模な Web アプリケーションで使用した別のアプローチは、ロギングによってログ メッセージ レコードをメモリ内リストに書き込むことです。次に、ログ メッセージをバックグラウンドで書き出すバックグラウンド スレッドを用意します。現在のソリューションでは、スレッドがリストを頻繁にチェックし、リストの長さが特定のしきい値を超えた場合、またはリストが特定の時間より長くフラッシュされていない場合、リストをディスク (この場合はデータベース) にフラッシュします。常に最初に来る。

これはプロデューサー/コンシューマー パターンのようなもので、コードでログ メッセージを生成し、コンシューマーはそれらのメッセージを永続メディアにフラッシュする責任があります。

于 2010-07-10T11:59:19.757 に答える