Разумное использование потоков в C #? - PullRequest
5 голосов
/ 27 марта 2009

В рамках большого процесса автоматизации мы вызываем сторонний API, который выполняет некоторые функции, вызывая службы на другом компьютере. Недавно мы обнаружили, что время от времени, когда другая машина недоступна, вызов API иногда прерывается до 40 минут при попытке подключения к удаленному серверу.

API, который мы используем, не предлагает способ указать время ожидания, и мы не хотим, чтобы наша программа долго ждала, поэтому я подумал, что потоки - это хороший способ установить время ожидания. Полученный код выглядит примерно так:

 Thread _thread = new Thread(_caller.CallServices());

 _thread.Start();
 _thread.Join(timeout);

 if (_thread.IsAlive)
 {
      _thread.Abort();
      throw new Exception("Timed-out attempting to connect.");
 }

По сути, я хочу, чтобы APICall () работал, но если он все еще работает после истечения времени ожидания, предположим, что он потерпит неудачу, убейте его и продолжайте.

Поскольку я новичок в потоках в C # и во время выполнения .net, я подумал, что задам два связанных вопроса:

Есть ли лучший / более подходящий механизм в библиотеках .net для того, что я пытаюсь сделать, и я зафиксировал какие-либо ошибки в этом фрагменте кода?

Ответы [ 5 ]

5 голосов
/ 27 марта 2009

Thread.Abort () - это запрос на прерывание потока, который не дает гарантии, что он сделает это своевременно. Это также считается плохой практикой (это вызовет исключение прерывания потока в прерванном потоке, но кажется, что сторонний API не предлагает вам другого выбора.

Если вы знаете (программно) адрес удаленного хоста службы, вы должны пропинговать его перед передачей управления стороннему API.

Если фоновый работник не используется, вы можете установить для IsBackgroundThread потока значение true, чтобы оно не препятствовало завершению вашей программы.

3 голосов
/ 27 марта 2009

Вы также можете просто использовать делегат ... Создайте делегат для метода, который выполняет работу, затем вызовите BeginInvoke для делегата, передав ему аргументы, и функцию обратного вызова для обработки возвращаемых значений (если хотите) ... Сразу же после BeginInvoke вы можете подождать в назначенное время, чтобы делегат асинхронности завершил свою работу, и, если это не произойдет в указанное время, двигаться дальше ...

   public delegate [ReturnType] CallerServiceDelegate
          ([parameter list for_caller.CallService]);

   CallerServiceDelegate callSvcDel = _caller.CallService;
   DateTime cutoffDate = DateTime.Now.AddSeconds(timeoutSeconds);
   IAsyncResult aR = callSvcDel.BeginInvoke([here put parameters], 
                                             AsynchCallback, null); 
   while (!aR.IsCompleted && DateTime.Now < cutoffDate)
       Thread.Sleep(500);
   if (aR.IsCompleted)
   {
      ReturnType returnValue = callSvcDel.EndInvoke(aR);
      // whatever else you need to do to handle success
   }
   else
   {
      callSvcDel.EndInvoke(aR);
      // whatever you need to do to handle timeout
   }

ПРИМЕЧАНИЕ: как написано, AsynchCallback может быть нулевым, так как код извлекает возвращаемое значение из EndInvoke (), но при желании вы можете заставить метод CallService () вызывать делегат AsynchCallback и передавать ему возвращаемые значения. ..

3 голосов
/ 27 марта 2009

Плохая идея. Thread.Abort не обязательно очищает беспорядок, оставленный таким прерванным вызовом API.

Если вызов дорогой, рассмотрите возможность создания отдельного .exe-файла, который выполняет вызов, и передайте аргументы в / из него с помощью командной строки или временных файлов. Вы можете уничтожить .exe гораздо безопаснее, чем уничтожить поток.

0 голосов
/ 27 марта 2009

Ваше приложение работает в течение продолжительных периодов времени или это скорее приложение, запускаемое по мере необходимости? Если это последнее, я лично рассмотрел бы использование опции Thread.Abort (). Хотя это может быть не самым желательным с точки зрения пуриста (управление ресурсами и т. Д.), Это, безусловно, легко реализовать и может оплатить расходы, учитывая работу вашего конкретного приложения.

Идея отдельного исполняемого файла имеет смысл. Возможно, другой вариант будет использовать AppDomains. Я не эксперт в этой области (я приветствую уточнения / исправления к этому), но, насколько я понимаю, вы бы поместили вызов API в отдельную DLL и загрузили его в отдельный домен приложений. Когда вызов API завершен или вам нужно прервать его, вы можете выгрузить AppDomain вместе с DLL. Это может иметь дополнительное преимущество в очистке ресурсов, чего не может быть у простого Thread.Abort ().

0 голосов
/ 27 марта 2009

Это может сработать, но никто не может сказать наверняка без понимания стороннего API. Подобный прерывание потока может привести к тому, что компонент окажется в каком-то недопустимом состоянии, из которого он не сможет восстановиться, или, возможно, он не освободит ресурсы, которые он выделил (подумайте - что делать, если одна из ваших подпрограмм просто перестала выполняться на полпути через . Не могли бы вы дать какие-либо гарантии о состоянии вашей программы?).

Как предположил Цицил, было бы неплохо сначала проверить связь с сервером.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...