Клиент WCF позволяет запускать одну операцию параллельно, но не другую?

#c# #multithreading #wcf-client

#c# #многопоточность #wcf-client

Вопрос:

У меня есть SOAP-клиент WCF, который я сгенерировал из service WSDL. Сервис предлагает, среди прочего, query и batch операции. Он также предполагает многопоточную обработку запросов.

При попытке распараллелить использование моего клиента я вижу странное поведение: клиент с радостью отправит столько query запросов, сколько я скажу ему параллельно, но он будет отправлять только один batch запрос за раз. Я использую Fiddler для просмотра трафика, и я вижу 10 открытых query запросов, но batch запросы всегда начинаются и заканчиваются по одному за раз.

Я использую Parallel.ForEach для обоих путей кода MaxDegreeOfParallelism значение 10. Я не меняюсь ServicePointManager.DefaultConnectionLimit , хотя я заметил, что если я установлю для него значение меньше, чем количество потоков query , запросы будут регулироваться. Я проверил, что TPL запускает несколько потоков, просмотрев ManagedThreadId значение в параллельных путях выполнения.

Насколько я могу судить, единственное отличие с точки зрения клиента заключается в том, что query использует метод HTTP GET и batch использует POST метод. Вызовет ли это поведение, которое я вижу? Есть ли что-то еще, на что я могу посмотреть?

РЕДАКТИРОВАТЬ В соответствии с запросом, вот соответствующий код клиента (я удалил ServiceKnownType атрибуты)

 //generated client interface
//{...
[OperationContract(Action = "", ReplyAction = "*")]
[FaultContract(typeof (fault), Action = "", Name = "fault")]
[XmlSerializerFormat(SupportFaults = true)]
[return: MessageParameter(Name = "return")]
QueryResponse query(QueryRequest queryRequest);

[OperationContract(Action = "", ReplyAction = "*")]
[FaultContract(typeof (fault), Action = "", Name = "fault")]
[XmlSerializerFormat(SupportFaults = true)]
[return: MessageParameter(Name = "return")]
BatchResponse batch(BatchRequest batchRequest);
//...}

//generated client class
//{...
public QueryResponse query(QueryRequest queryRequest)
{
  return this.Channel.query(queryRequest);
}

public BatchResponse batch(BatchRequest batchRequest)
{
  return this.Channel.batch(batchRequest);
}
//...}
  

ПРАВКА2
Вот мой вывод отладки, пытаюсь отследить это.

Creating...{Thread Id} записывается на консоль непосредственно перед тем, как я начну собирать полезную нагрузку для batch операции

Sending...{Thread Id} строки записываются в консоль непосредственно перед batch вызовом операции

Sent...{Thread Id} строки записываются в консоль сразу после завершения batch операции

 1:53:58 PM (Thread 4)
1:53:58 PM (Thread 19)
1:53:58 PM (Thread 20)
1:53:58 PM (Thread 16)
1:53:58 PM (Thread 18)
1:53:58 PM (Thread 17)
1:53:58 PM (Thread 7)
1:53:58 PM (Thread 15)
Creating...19
Creating...15
Creating...16
Creating...17
Creating...7
Creating...4
Creating...20
Creating...18
Sending...17
Sending...7
Sending...20
Sending...19
Sending...16
Sending...18
Sending...4
Sending...15
1:53:59 PM (Thread 22)
Creating...22
Sending...22
1:53:59 PM (Thread 23)
Creating...23
Sending...23
Sent...17
Sent...15
Sent...7
Sent...18
Sent...20
Sent...16
Sent...4
Sent...19
Sent...22
Sent...23
  

И вот журнал скрипача для этого запуска:

введите описание изображения здесь

Для справки, вот фрагмент из журнала скрипача для query пути к коду:

введите описание изображения здесь

Комментарии:

1. Вы контролируете службу? Служба может быть настроена на обработку только одного вызова за раз. См. Главу 8 в книге Juval «Программирование служб WCF».

2. @EricScherrer Нет — услуга предоставляется третьей стороной. Даже если бы служба регулировала, я все равно ожидал бы увидеть запросы на подключение, ожидающие в Fiddler, если клиент выполнял несколько одновременных запросов.

3. Можете ли вы опубликовать созданный клиент? Интересно, есть ли какие-либо интересные атрибуты в этом методе.

4. @EricScherrer Я обновил вопрос этим кодом. В методах класса нет атрибутов, но я включил все, кроме ServiceKnownType атрибутов в методах интерфейса.

5. Я не уверен, что на самом деле нет никакой разницы в том, как вы обрабатываете эти два метода. Попробуйте создать минимальный фрагмент кода, чтобы воспроизвести это. Просто вызовите Parallel . ForEach с выдуманной рабочей нагрузкой и однострочным телом, вызывающим службу. Опубликуйте код здесь.