私は非同期操作を定義するコントラクトを持っていますが、私は非同期として設定されていない領域から呼び出しています。私は、並行して複数の呼び出しを作りたいので、私はこれを開始しました:私はそれが並列にすべてのコールを開始した後、第2ラインで待機していました考え出し非同期ですが非同期です
var tasks = inputs.Select(input => service.GetResult(input));
var results = tasks.WhenAll(tasks).Result;
。しかし、対象のサービスのログを見て、私は呼び出しが連続して来ていることを発見しました。
は、私は私に呼び出すのと同様の方法を示し、それは必ずしも並行して実行されないことを説明してthis articleを見つけたので、私はちょうどテストにまっすぐ平行にコールにそれを切り替える:
var results = new ConcurrentBag<Result>();
Parallel.ForEach(inputs, input => results.Add(service.GetResult(input).Result));
この作品期待どおり - 私は呼び出しが並行してサービスに来ているのを見ることができます。
だから、これは二つの質問に私をもたらします:
1)オプション2と一緒に行くの欠点は何ですか?
2)オプション1を正しく動作させるにはどうすればよいですか?
ここでは、問題を再現するためのサービスがいくつかあります。 WCFTestClientを使用してClientServiceを呼び出し、例として4つのint(1、2、3、4)のリストを呼び出します。 (ポートはおそらく、あなたがそれを実行したときに変更する必要があります。)
は、TargetService:
using System.Diagnostics;
using System.ServiceModel;
using System.Threading.Tasks;
namespace AsyncNotParallel
{
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall, ConcurrencyMode = ConcurrencyMode.Single)]
public class TargetService : ITargetService
{
public async Task<int> GetResult(int input)
{
Trace.WriteLine($"In: {input}");
await Task.Delay(1000); // Do stuff.
Trace.WriteLine($"Out: {input}");
return input;
}
}
[ServiceContract]
public interface ITargetService
{
[OperationContract]
Task<int> GetResult(int input);
}
}
ClientService:
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.ServiceModel;
using System.Threading.Tasks;
namespace AsyncNotParallel
{
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall, ConcurrencyMode = ConcurrencyMode.Single)]
public class ClientService : IClientService
{
public int GetResults(List<int> inputs)
{
// Option 1:
var tasks = inputs.Select(input => Execute((ITargetService service) => service.GetResult(input)));
var results1 = Task.WhenAll(tasks).Result.Sum();
// Option 2:
var bag = new ConcurrentBag<int>();
Parallel.ForEach(inputs, input => bag.Add(Execute((ITargetService service) => service.GetResult(input)).Result));
var results2 = bag.Sum();
return results1 + results2;
}
private TResult Execute<TService, TResult>(Func<TService, TResult> operation)
{
var address = new EndpointAddress("http://localhost:34801/TargetService.svc");
var binding = new BasicHttpBinding();
var factory = new ChannelFactory<TService>(binding, address);
var channel = factory.CreateChannel();
var result = operation(channel);
((IClientChannel)channel).Close();
return result;
}
}
[ServiceContract]
public interface IClientService
{
[OperationContract]
int GetResults(List<int> inputs);
}
}
.Resultを使用しないでください。常に非同期呼び出しを待っています。 – Bart
@Bart: ".Resultを使わない"ためには、クライアント全体をasync/awaitに変更する必要がありますが、これは非同期契約がある唯一の場所です。私は自分のローカルコピーを(共有DLLにあります)パラレルコールにすることはできますが、私の質問には答えず、ここで何が起こっているのか理解できません。 – zimdanen
asycnは並列と同じものではありません。 AsycnはIOが発生している間にスレッドをブロックしないようにしていますが、並列は複数のスレッドで実行しています。非同期的に別のスレッドが終了するのを待つことができるという点で重複していますが、非同期の主な目的はスレッドが不必要にブロックされないようにすることです。 – juharr