3

IIS6 でホストされている WCF サービスがあります。メソッドの重要な部分は次のようになります。

public MyUser[] GetUsers(string appName, string[] names)
{
    List<User> users = new List<User>();
    foreach (string user in names)
    {
      MembershipUser mu = this.ADAMProvider.GetUser(user, false);  //Unmanaged call to AzMan
      if (mu != null)
      {
        users.Add(MyUser.CreateFrom(mu);
      }
    }
    return users.ToArray();
}

このメソッドのパフォーマンスは、多数のユーザー名 (100 程度以上) で呼び出されると非常に悪くなります。戻るまでに 1 分以上かかる場合があります。また、このメソッドが複数のクライアントから同時に呼び出されると、タイムアウトになります。アプリプールがダウンするのを見たことさえあります。ループ内で AzMan への呼び出しが行われていることに注意してください。AzMan はアンマネージ COM コンポーネントです。

パフォーマンスを向上させるために、マルチスレッドのアプローチを検討しています。.NET 4 はオプションではないため、Parallel.For はオプションではありませんが、3.5 で同等のことを行うことは可能です。

私の質問は、一連のスレッドを作成して (すべてを待機してから戻る)、実際にパフォーマンスが向上するかどうかです。IIS6 でホストされている WCF サービスでこれを行うことには危険がありますか?

4

2 に答える 2

3

まず、アパートメントの状態によっては、COM コンポーネントが問題になる可能性があることを指摘しました。シングルスレッド アパートメント オブジェクトは、1 つのスレッドでのみ実行できます。STA オブジェクトで発生する自動マーシャリング操作があり、すべての呼び出しを効果的にシリアル化するため、どんなに頑張っても並列化できない可能性があります。それが MTA オブジェクトであったとしても、GetUserメソッドがスレッド セーフになるように設計されていなければ、メソッドに問題がある可能性があります。

しかし、これが問題ではないと仮定すると、これを行うためにスレッドの束を作成する代わりに を使用します。ThreadPoolこれがどのように見えるかです。

public MyUser[] GetUsers(string appName, string[] names) 
{ 
  int count = 1; // Holds the number of pending work items.
  var finished = new ManualResetEvent(false); // Used to wait for all work items to complete.
  var users = new List<User>(); 
  foreach (string user in names) 
  {
    Interlocked.Increment(ref count); // Indicate that we have another work item.
    ThreadPool.QueueUserWorkItem(
      (state) =>
      { 
        try
        {
          MembershipUser mu = this.ADAMProvider.GetUser(user, false); 
          if (mu != null) 
          { 
            lock (users)
            {
              users.Add(MyUser.CreateFrom(mu); 
            }
          } 
        }
        finally
        {
          // Signal the event if this is the last work item.
          if (Interlocked.Decrement(ref count) == 0) finished.Set();
        }
      });
  } 
  // Signal the event if this is the last work item.
  if (Interlocked.Decrement(ref count) == 0) finished.Set();
  // Wait for all work items to complete.
  finished.WaitOne();
  return users.ToArray(); 
}

上で使用したパターンで紛らわしい点の 1 つは、メイン スレッド (作業をキューに入れているスレッド) を別の作業項目であるかのように扱うことです。そのため、ループの最後にチェック コードとシグナル コードが表示されます。これがないと、SetとのWaitOne呼び出しの間で非常に微妙な競合状態が発生する可能性があります。

ところで、TPL はReactive Extensionsのダウンロードの一部として .NET 3.5 で利用できることを指摘しておく必要があります。

1私が言及した 2 つの問題のうちの 1 つが実際に作用すると思われます。

于 2010-11-05T16:05:50.383 に答える
2

通常、これは役立つかもしれませんが、これは懸念事項です。

また、このメソッドが複数のクライアントから同時に呼び出されると、タイムアウトになります。アプリプールがダウンするのを見たことさえあります。ループ内で AzMan への呼び出しが行われていることに注意してください。AzMan はアンマネージ COM コンポーネントです。

「AzMan」コンポーネントはスレッドセーフではないようです。その場合、このルーチンでほとんどの時間を費やしているため、このルーチンを効果的にマルチスレッド化することはできません。

ただし、そのルーチンがスレッド セーフであり、状態を共有しない場合は、マルチスレッドによってパフォーマンス向上する可能性があります。ただし、マシン自体のワークロード (すべてのコアが十分に活用されている場合は、役に立たない可能性があります) など、他の多くの問題に依存します。

于 2010-11-05T15:53:13.423 に答える