2016-07-11 9 views
3

私は、識別子のデータを得ることができるWebサービスを持っています(顧客IDの請求情報など)。私のクライアントには複数のスレッドがあります。これらのスレッドは、Webサービスのメソッドを呼び出して、識別子のデータを取得します。1つの結果に対して複数のスレッドを待ちます

たとえば、CustomerID = 12345のようなWebサービスへのリクエスト中に、別のスレッドが同じ顧客のWebサービスを呼び出そうとしたときに発生します。今私は、要求の結果をリサイクルするためにサーバーを確保したいと思います。 したがって、私は最初のリクエストの結果が得られるまで2番目のスレッドを待って、結果を2番目のスレッドにも返したいと思います。

重要な要件は、2番目のスレッドがWeb要求が返された後にデータを取得する場合は、新しい要求を送信する必要があるということです。私の問題を説明するために、私はsequence diagramを表示します:

私はこれをC#でどのように達成できるか説明できますか?

+0

以下のリンクは役に立つかもしれません。(http://www.codeproject.com/Questions/1009076/Concurrent-Web-Request-Csharp) – Dandy

+0

私がどのように役立つか分かりません。なぜなら、投稿したくないからですいくつかの要求はそれらを防ぐために置く。 – scher

+1

多分タスクキャッシングがあなたを助けることができますか? http://stackoverflow.com/questions/36084495/when-to-cache-tasks –

答えて

0

は、最後に私は、次の解決策で終了:

private class RequestHandler 
{ 
    private EventWaitHandle _handle; 

    public RequestHandler(Func<string> request) 
    { 
     _handle = new EventWaitHandle(false, EventResetMode.ManualReset); 
     Start(request); 
    } 

    private void Start(Func<string> doRequest) 
    { 
     Result = doRequest(); 
     _handle.Set(); 
    } 

    public void WaitForResponse() 
    { 
     _handle.WaitOne(); 
    } 

    public string Result { get; private set; } 
} 

private Object _lockObject = new Object(); 
private Dictionary<string, RequestHandler> _pendingRequests = new Dictionary<string, RequestHandler>(); 

public string GetCustomer(int id) 
{ 
    RequestHandler request; 

    lock (_lockObject) 
    { 
     if (!_pendingRequests.TryGetValue(id, out request)) 
     { 
      request = new RequestHandler(() => LoadEntity(id)); 
      _pendingRequests[id] = request; 
     } 
    } 

    request.WaitForResponse(); 
    lock (_lockObject) 
    { 
     _pendingRequests.Clear(); 
    } 
    return request.Result; 
} 

それはサーバーへの投稿を重複していることはまだ可能ですが、これは非常に最小限のチャンスです(スレッド上で作成された最初のスレッドの後に、すべての保留中の要求を削除します要求と2番目のスレッドが同じ顧客データを取得しようとする前)。

インスピレーションのおかげで、ここに投稿されました。

0

私はあなたがこの(擬似コード)のようにそれを行うことができると思います。

  • アクションと実行Taskを保存するために辞書を作成します。
  • 要求を受け取ったときに、辞書を一時的にロックして、同時に2つのタスクを取得しないで、タスクを作成して辞書に追加します。
  • awaitタスク(またはスレッド)。

  • 別のリクエストが入った場合は、まず辞書の実行中のタスクを確認します。それがあれば、それも待っています。そうでない場合は、新しいものを作成します。

このような何か:

Dictionary<string, Task<string>> tasks = new Dictionary<string, Task<string>>(); 

public async Task<string> GetCustomer(int id) 
{ 
    string taskName = $"get-customer-{id}"; 

    try 
    { 
     Task<string> task; 

     lock (tasks) 
     { 
      if (!tasks.TryGetValue(taskName, out task)) 
      { 
       task = new Task<string>(() => GetCustomerInternal(id)); 
       tasks[taskName] = task; 
      } 
     } 

     string result = await task; 

     return result; 
    } 
    finally 
    { 
     lock (tasks) 
     { 
      tasks.Remove(taskName); 
     } 
    } 
} 

private string GetCustomerInternal(int id) 
{ 
    return "hello"; 
} 
+0

私の答えはちょうど長い... – user1012506

+0

OK、 もう一度見てください... – user1012506

+0

あなたの答えは、キーワードの使用を指しているので、何の説明をしなくても意味がないので、無用です。それはちょうどコメントだったかもしれません。 –

1

あなたはこのような何か行うことができます。

private static ConcurrentDictionary<string, Task<string>> s_urlToContents; 

public static Task<string> GetContentsAsync(string url) 
{ 
    Task<string> contents; 
    if(!s_urlToContents.TryGetValue(url, out contents)) 
    { 
     contents = GetContentsAsync(url); 
     contents.ContinueWith(t => s_urlToContents.TryAdd(url, t); }, 
     TaskContinuationOptions.OnlyOnRanToCompletion | 
     TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Default); 
    } 
    return contents; 
} 

private static async Task<string> GetContentsAsync(string url) 
{ 
    var response = await new HttpClient().GetAsync(url); 
    return response.EnsureSuccessStatusCode().Content.ReadAsString(); 
} 

別のスレッドが同じ要求へのアクセスを望んでいる場合は、このコードは、HTTPリクエストをキャッシュし、あなたをすでにキャッシュされているタスクを返します。存在しない場合は、タスクを生成し、将来の要求のためにキャッシュします。

+0

あなたはまだ2つのタスクを同時に実行することができます。両方とも 'if(!s_urlToContents.TryGetValue(url、out contents))'を同時に渡すと、両方の実行でタスクを作成します。 –

関連する問題