2017-11-09 31 views
1

短い紹介: 私は、さまざまな時間間隔で機能している他のアプリケーションとサービスを監視するWindowsサービスを用意しています。 このサービスは、監視対象アプリケーション(「monitor」という名前)ごとに1つのタイマー(System.Threading.Timer)を使用します。 異なるタイプのアプリケーションでは、さまざまなタイプのモニターが必要です。一部は同期して動作し、他のタイプは非同期(たとえば、HttpClientを使用するもの)です。System.Threading.Timer内での非同期操作の異常な動作

私はタイマーで非同期呼び出しが必要なところまで行きました。 私はここにそれを掲示できるように制限にコードを単純化しました。これはコンソールプロジェクトに直接実行できます。 私の問題は、より多くのタイマが導入されているので、このコードは非常に奇妙な動作をしているということです。 モニターの実行時間は、非同期操作(100ms)で設定された遅延とまったく同じではありませんか?

using System; 
using System.Collections.Generic; 
using System.Diagnostics; 
using System.Threading; 
using System.Threading.Tasks; 

namespace TestMain 
{ 
    class TestMain 
    { 
     private static List<TestTimer> timers = new List<TestTimer>(); 

     static void Main(string[] args) 
     { 
      for (int i = 0; i < 20; i++) 
      { 
       TestMain.timers.Add(new TestTimer(i)); 
      } 
      Console.WriteLine("Press [Enter] to exit."); 
      Console.ReadLine(); 
     } 

     public class TestTimer 
     { 
      public Int32 Id { get; private set; } 
      private Timer timer; 

      public TestTimer(Int32 id) 
      { 
       this.Id = id; 
       this.timer = new Timer(this.Test, null, 1000, 30 * 1000); 
      } 

      private void Test(Object state) 
      { 
       TestWorker t = new TestWorker(this.Id); 
       t.Run(); 
      } 
     } 

     public class TestWorker 
     { 
      public Int32 Id { get; private set; } 
      private Stopwatch sw = new Stopwatch(); 

      public TestWorker(Int32 id) { this.Id = id; } 

      public void Run() 
      { 
       this.RunAsync().Wait(); 
      } 
      private async Task RunAsync() 
      { 
       this.Log(String.Format("Start[{0,2}]", this.Id)); 
       this.sw.Restart(); 

       await Task.Run(() => { System.Threading.Thread.Sleep(100); }).ConfigureAwait(false); 

       this.sw.Stop(); 
       this.Log(String.Format(" End[{0,2}] Duration=[{1}]", this.Id, (Int32)this.sw.ElapsedMilliseconds)); 
      } 
      private void Log(String text) 
      { 
       Console.WriteLine(String.Format("{0,20} {1}", DateTime.Now, text)); 
      } 

     } 
    } 
} 

印刷画面を実行しました。 Console Printscreen

+0

非同期操作の後でもはや待機していないソリューションは、問題のいくつかを完了しますが、依然としてスレッド間にボトルネックが存在します。 私にとって適切な解決策は、サービスの開始時に監視する必要のあるアプリケーション(番号とタイプ)がすべて分かっており、スレッドの最小数が可能であるため、時間の経過とともにスケーラブルであるため、ThreadPool.SetMinThreadsを使用します。それに応じて設定します。 答えをありがとう。 –

答えて

0

を行う代わりに、

public void Run() 
{ 
    RunAsync().Wait(); 
} 

の、何を待っていませんスレッドプールはそのスレッドを管理しました。スレッドプールには「最小限」のスレッド数(ThreadPool.GetMinThreadsで読むことができます)があります。デフォルトでは(これは.NETのバージョンに依存しますが、それを複雑にすることはありません)、これはプロセッサコアの数に関連しています。たとえば、マシンが8の場合です。これらの8つのスレッドがビジー状態で、最初にビジー状態のスレッドの1つが使用可能になるまで待ってください(約1秒間待機します)。スレッドが使用できない場合は、プールにスレッドをもう1つ追加します。

タイマコールバックはスレッドプール上で実行されます。だから、あなたのタイマーの20すべてが同時にコールバックを起動するとき - 私の場合は8コールバックだけが実行されます。残りはキューに入れられ、1秒ごとに実行されます(スレッドプールからスレッドを実行するように要求しますが、スレッドプール内のすべてのスレッドがその時点でビジー状態であるため、毎回1秒間待機します)。あなたのタイマーコールバックは、RunAsyncWait()で完了するのを待っているので、忙しいです。したがって、12(20-8)秒後にのみ、すべてのタイマーコールバックが実行されます。

タイマーコールバックが実行されると、コンソールにStartメッセージが書き込まれ、がストップウォッチを開始します。次に、Task.Runを実行して、スレッドプールから別のスレッドを要求します。これらの要求はすべて、タイマーコールバックの後にキューに入れられます。したがって、すべてのタイマーが開始された後で初めて、Endメッセージを受信し始めます。

これで、RunAsyncが完了するのを待って、20スレッドがビジー状態になりました。最初にTask.Runが別のスレッドを要求します。このスレッドは100ミリ秒待機した後、フリーで再利用できるので、タスクプールはそれぞれTask.Runの新しいスレッドを作成せず、このスレッドを再利用します(100ミリ秒は1秒より短いので、スレッドが使用可能になるのを待つため)。

ThreadPool.SetMinThreadでスレッドプールの最小スレッドをより大きな値に設定するか、RunAsyncが完了するのを待ってタイマーコールバックスレッドを保持しないでください。

0

System.Threading.Timerスレッドプールが使用されているため、スレッド数に制限があり、これがあなたの経験です。

モニタの実行時間は、非同期動作(100ms)で設定された遅延とまったく同じではありませんか?何をしたいのですが、内部のタスクは、スレッドプールからのスレッドを使用したいので。スレッドは、さらに多くの完了とするタスクを待っている期間中の忙しいようです

クイックフィックスは、(credits)ファイア・アンド・フォーゲットメソッドを使用することであるが、このようタイマーがあるためどのようにのだ

public void Run() 
{ 
    #pragma warning disable 4014 
    RunAsync(); 
    #pragma warning restore 4014 
} 
関連する問題