2016-01-24 35 views
5

私は長い時間を実行するシングルスレッドプロセスを持っています。私はこのプロセスを実行するために複数のユーザーにアクセスする必要があり、私は呼び出しを管理するためにhttpプロトコルを選択します。HttpListenerのマルチスレッド応答

もちろん、1つのプロセスが動作しているときは、他のすべてのプロセスが完了するまで待つべきです。プロセスが利用可能であれば、それは実行されます。そうでない場合は、BUSY応答が送信されます。

using System; 
using System.Net; 
using System.Reflection; 
using System.Runtime.InteropServices; 
using System.Threading; 
using System.Threading.Tasks; 

namespace simplehttp 
{ 
    class Program 
    { 
     private static System.AsyncCallback task; 
     private static System.Threading.ManualResetEvent mre = new System.Threading.ManualResetEvent(false);// Notifies one or more waiting threads that an event has occurred. 
     private static HttpListenerContext workingContext = null; 

     public static bool isBackgroundWorking() 
     { 
      return mre.WaitOne(0); 
     } 
     static void Main(string[] args) 
     { 
      new Thread(() => 
      { 
       Thread.CurrentThread.IsBackground = true; 
       while (true) 
       { 
        Console.WriteLine(" waitOne " + isBackgroundWorking()); 
        mre.WaitOne(); // Blocks the current thread until the current WaitHandle receives a signal. 
        Console.WriteLine(" do job" + " [" + Thread.CurrentThread.Name + ":" + Thread.CurrentThread.ManagedThreadId + " ]\n"); 
        HttpListenerRequest request = workingContext.Request; 
        HttpListenerResponse response = workingContext.Response; 
        string responseString = "WORK " + DateTime.Now ; 
        byte[] buffer = System.Text.Encoding.UTF8.GetBytes(responseString); 
        response.ContentLength64 = buffer.Length; 
        System.IO.Stream output = response.OutputStream; 
        Thread.Sleep(10000); 
        output.Write(buffer, 0, buffer.Length); 
        output.Close(); 
        Console.WriteLine(" " + responseString + "\t" + DateTime.Now); 
        workingContext = null; 
        mre.Reset(); // Sets the state of the event to nonsignaled, causing threads to block. 
       } 
      }).Start(); 

      // Create a listener. 
      HttpListener listener = new HttpListener(); 

      listener.Prefixes.Add("http://localhost:6789/index/"); 
      listener.Start(); 
      Console.WriteLine("Listening..." + " [" + Thread.CurrentThread.Name + ":" + Thread.CurrentThread.ManagedThreadId + " ]\n"); 

      task = new AsyncCallback(ListenerCallback); 

      IAsyncResult resultM = listener.BeginGetContext(task,listener); 
      Console.WriteLine("Waiting for request to be processed asyncronously."); 

      Console.ReadKey(); 
      Console.WriteLine("Request processed asyncronously."); 
      listener.Close(); 
     } 

     private static void ListenerCallback(IAsyncResult result) 
     { 
      HttpListener listener = (HttpListener) result.AsyncState; 

      //If not listening return immediately as this method is called one last time after Close() 
      if (!listener.IsListening) 
       return; 

      HttpListenerContext context = listener.EndGetContext(result); 
      listener.BeginGetContext(task, listener); 

      if (workingContext == null && !isBackgroundWorking()) 
      { 
       // Background work 
       workingContext = context; 
       mre.Set(); //Sets the state of the event to signaled, allowing one or more waiting threads to proceed. 
      } 
      else 
      { 
      HttpListenerRequest request = context.Request; 
      HttpListenerResponse response = context.Response; 
      string responseString = "BUSY "+ DateTime.Now + " [" + Thread.CurrentThread.Name + ":" + Thread.CurrentThread.ManagedThreadId; 
      byte[] buffer = System.Text.Encoding.UTF8.GetBytes(responseString); 
      response.ContentLength64 = buffer.Length; 
      System.IO.Stream output = response.OutputStream; 
      output.Write(buffer, 0, buffer.Length); 
      output.Close(); 
      Console.WriteLine(responseString + "\t" + DateTime.Now); 
      } 
     } 
    } 
} 

私は2つのHTTP呼び出しを行うテストするには:ここでは

は実装です。私は2つの異なる答えがあると思うWORKとBUSY。 しかし、2番目のリクエストが最初に終了してから実行されるのを待っています。

 waitOne False 
Listening... [:10 ] 

Waiting for request to be processed asyncronously. 
     do job [:11 ] 

     WORK 1/24/2016 10:34:01 AM 1/24/2016 10:34:11 AM 
     waitOne False 
     do job [:11 ] 

     WORK 1/24/2016 10:34:11 AM 1/24/2016 10:34:21 AM 
     waitOne False 

どうすればうまくいくはずですか?

更新(あまりにも多くのコメントは、SOによってecouragedされていません): 私のコードは、実際のプロセスの複製ですので、厄介なように見えます。 「私の」アプリケーションでは、作業プロセスは、特定の瞬間に埋め込まれたC#コードを実行する「礼儀」を持っている主要なプロセスです。したがって、私は要求を処理するための新しいタスクを実行することはできませんし、作業プロセスが独自の仕事をし、データが利用可能であるときにクライアントに通知するためにスレーブコードを呼び出すだけなので、非同期的でなければなりません。コードが呼び出され、できるだけ早く終了するか、マスターアプリケーションをブロックするため、非同期です。 私は同期呼び出しでスレッドを追加しようとすると、状況に影響を与えるホット参照してください。

この例では、コンソールに出力されたリアルタイムのプロセスとタイムスタンプを妨害しないためにデバッガは使用されません。デバッグはすばらしく、必要ですが、この場合は、同期/待機のシナリオで余分なアクタを避けるために出力に置き換えようとします。

アプリケーション自体は負荷の高い会話ではありません。 1-3人のクライアントは、メインアプリケーションに回答することはめったにありません。 httpプロトコルは、重い、またはしばしば会話のためではなく便宜のために使用されます。 IEのようないくつかのブラウザ(WindowsからWindowsへの会話?)とChrome(よりシステムに依存しないもの)のようなものは、私のアプリケーションの動作を複製するように見えます。タイムスタンプ、Chrome、IE、IE、Chromeを見て、最後のChromeはまだWORKプロセスに行きました。ところで、会話の提案につきコードが変更され、新しいリクエストは直前のリクエストを検索した直後に配置されます。

HttpListenerContext context = listener.EndGetContext(result); 
    listener.BeginGetContext(task, listener); 

enter image description here

また、提案以下、私は完全に読み、そのコードを理解していないsyncroniuosにasyncroniousコールを変更し、結果はまだ同じ

private static void ListenerCallback(IAsyncResult result) 
{ 
    HttpListener listener = (HttpListener) result.AsyncState; 

    //If not listening return immediately as this method is called one last time after Close() 
    if (!listener.IsListening) 
     return; 

    HttpListenerContext context = listener.EndGetContext(result); 

    while (true) 
    { 
     if (workingContext == null && !isBackgroundWorking()) 
     { 
      // Background work 
      workingContext = context; 
      mre.Set(); //Sets the state of the event to signaled, allowing one or more waiting threads to proceed. 
     } 
     else 
     { 
      HttpListenerRequest request = context.Request; 
      HttpListenerResponse response = context.Response; 
      string responseString = "BUSY " + DateTime.Now + " [" + Thread.CurrentThread.Name + ":" + 
            Thread.CurrentThread.ManagedThreadId; 
      byte[] buffer = System.Text.Encoding.UTF8.GetBytes(responseString); 
      response.ContentLength64 = buffer.Length; 
      System.IO.Stream output = response.OutputStream; 
      output.Write(buffer, 0, buffer.Length); 
      output.Close(); 
      Console.WriteLine(responseString + "\t" + DateTime.Now); 
     } 
     context=listener.GetContext(); 
    } 
} 

答えて

2

でいました。それは構造が厄介です。

while (true) { 
var ctx = GetContext(); 
Task.Run(() => ProcessRequest(ctx)); 
} 

これは単純にすべての受信作業をディスパッチします。次に:

object @lock = new object(); //is work being done? 
void ProcessRequest(Context ctx) { 
if (!Monitor.Enter(@lock)) 
    SendBusy(); 
else { 
    ProcessInner(ctx); //actually do some work 
    Monitor.Exit(@lock); 
} 
} 

これは本当に必要なすべてです。

特に、非同期IOを使用するのは無意味です。私はあなたがそのコードやアイデアをどこかからコピーしたと仮定します。よくある間違い。 Async IOは、ここで何も役立たず、コードを複雑にしません。まさにそれが必要のような投稿作品として

+0

これは重要な情報です。だからあなたが投稿したコードについては何が真実で、何が虚偽ですか?私はどのような変化を提案でき、どこを見ていくのか分からない。 – usr

+0

EndGCの直後にBeginGetContextを呼び出す必要がある理由が1つあります。コードは今のところ、間に多くの作業が行われています。 – usr

+0

Btw、デバッガを使って何が起こったのか確認しましたか? – usr

4

コード:

enter image description here

を再現することはできません。明らかにテストワークロードを間​​違って運転しているので、質問に答えていると思います。私はFiddlerのコンポーザー送信ボタンを繰り返しクリックしてそれを運転しました。

実行可能コードを送信していただきありがとうございます。私は早くそれを試していたはずです!

+0

2つのChromesを開いてもう1つのボタンをクリックして2秒後にもう一度クリックします。どちらも即座に「Waiting answer」のステータスになりました。スクリーンショットが必要ですか? :)私は同意する - それは時々動作する。ほとんどの場合でも。私が掘り出し始めるのは、生産現場ではかなり頻繁に起こるということです。私はコードを抽出し、それが動作しないことを示すためにかなり良い仕事をしたと思います。もちろん、それがPhotoshopだと思っていない限り、私は本当に感謝している答えを混乱させる人を楽しくしています。 – Alex

+0

ええええええええええええええええええええええええ:それは理解しやすく、再現するのがより簡単な、よりクリーンな方法でしょう。実際、私はChromeを使って同じ動作をしますが、FiddlerはChromeがリクエストを順番に処理していることを示しています。理由は分かりませんが、それはアプリのバグではありません。 – usr

+0

IEは期待どおりに動作します。 – usr

関連する問題