2016-06-14 35 views
5

Mam jedną aplikację (UWP - Win10) i usługę Windows.Oczekiwanie na zadanie serwisowe otrzymuje TaskCanceledException: zadanie zostało anulowane

Usługa działa w tle, a obie zostały opracowane w języku C#. "callAsync" to metoda w usłudze. Czekam na telefon, aby zadzwonić do klienta.

var obj = await callAsync(10); 

Problem polega na: Jeśli ta rozmowa trwa mniej niż 1min40s (100 sekund), następnie wszystko działa OK. Ale jeśli zajmie to więcej niż 1min40s, nastąpi wyjątek "TaskCanceledException: zadanie zostało anulowane".

Mam wyszukiwania SO i sieci, ale nadal nie mogłem znaleźć wskazówkę, jak rozwiązać ten problem "limit czasu". Dodałem wszystkie flagi timeout "otwórz/zamknij/odbierz/wyślij" na app app i app.config aplikacji, chociaż wyjątek, który jest zgłaszany w tym przypadku, jest inny.

Gdy próbuję z prostego opóźnienia w kliencie:

await Task.delay(200000); 

działa prawidłowo.

Ta usługa została dodana przez VS2015 "Dodaj referencję serwisową". Mam również "dołączony" do serwera i serwer nadal działa i drukuje w konsoli przed i po logach (aby potwierdzić, że wszystko jest w porządku).

Czego mi brakuje? Jaką konfigurację i gdzie muszę zmienić, aby zadanie mogło działać dłużej niż 1 minutę i 40 sekund?

KOD:

Przykład Server Pseudo-Code:

Interfejs pliku:

[ServiceContract(Namespace="http://.....")] 
interface ICom { 

    [OperationContract] 
    int call(int val); 

} 

Service.cs

public ServiceHost serviceHost = null; 
    public BlaBlaWindowsService() 
    { 
     ServiceName = "BlaBlaWindowsService"; 
    } 

    public static void Main() 
    { 
     ServiceBase.Run(new BlaBlaWindowsService()); 
    } 


    protected override void OnStart(string[] args) 
    { 
     if (serviceHost != null) 
     { 
      serviceHost.Close(); 
     } 

     serviceHost = new ServiceHost(typeof(BlaBlaService)); 

     serviceHost.Open(); 
    } 

    protected override void OnStop() 
    { 
     if (serviceHost != null) 
     { 
      serviceHost.Close(); 
      serviceHost = null; 
     } 
    } 
} 

[RunInstaller(true)] 
public class ProjectInstaller : Installer 
{ 
    private ServiceProcessInstaller process; 
    private ServiceInstaller service; 

    public ProjectInstaller() 
    { 
     process = new ServiceProcessInstaller(); 
     process.Account = ServiceAccount.LocalSystem; 
     service = new ServiceInstaller(); 
     service.ServiceName = "BlaBlaWindowsService"; 
     Installers.Add(process); 
     Installers.Add(service); 
    } 
} 

BlaBlaService.cs

class TPAService : ITPAComunication { 

    public int call(int val) { 

     System.Threading.Thread.Sleep(200000) 
     return 0; 
    } 

} 

App.config plików

<?xml version="1.0" encoding="utf-8" ?> 
<configuration> 
<system.serviceModel> 
<bindings> 
<binding name="ServiceTimeout" closeTimeout="00:10:00" receiveTimeout="00:10:00" openTimeout="00:10:00" sendTimeout="00:10:00"/> 
</bindings> 
     <services> 
      <service name="BlaBla.Service.Service" 
        behaviorConfiguration="ServiceBehavior"> 
      <host> 
       <baseAddresses> 
       <add baseAddress="http://localhost:8000/BlaBla/service"/> 
       </baseAddresses> 
      </host> 
      <endpoint address="" 
         binding="basicHttpBinding" 
         bindingConfiguration="ServiceTimeout" 
         contract="BlaBla.Service.ICom" /> 
      <endpoint address="mex" 
         binding="mexHttpBinding" 
         contract="IMetadataExchange" /> 
      </service> 
     </services> 
     <behaviors> 
      <serviceBehaviors> 
      <behavior name="ServiceBehavior"> 
       <serviceMetadata httpGetEnabled="true"/> 
       <serviceDebug includeExceptionDetailInFaults="False"/> 
      </behavior> 
      </serviceBehaviors> 
     </behaviors> 
     </system.serviceModel> 
    </configuration> 

Przykład App pseudokodzie:

System.ServiceModel.EndpointAddress epa = new System.ServiceModel.EndpointAddress("http://localhost:8000/blabla/service"); 

System.ServiceModel.BasicHttpBinding bhb = new System.ServiceModel.BasicHttpBinding(); 

Timespan t = new TimeSpan(0, 10, 0); 

bhb.SendTimeout = t; bhb.ReceiveTimeout =t; bhb.OpenTimeout = t; bhb.CloseTimeout = t; 

Blabla.ComunicationClient com = new Blabla.ComunicationClient(bhb, epa); 

var obj = await com.callAsync(int val); 

return obj; 

AKTUALIZACJA # 1

Taka sytuacja ma miejsce tylko w UWP. Stworzyłem podobny projekt WinForms i wszystko działa zgodnie z oczekiwaniami. Oznacza to, że prawdopodobnie jest to coś związanego z UWP.

+0

Prawdopodobnie dzieje się tak z powodu przekroczenia limitu czasu w żądaniu WWW: https://msdn.microsoft.com/en-us/library/system.net.httpwebrequest.timeout%28v=vs.110%29.aspx? f = 255 & MSPPError = -2147217396 – Bas

+0

@Bas Dzięki za komentarz, ale limit czasu (zgodnie z dokumentacją, którą właśnie połączono, generuje wyjątek WebException. Przypadek, o którym wspomniałem, wywołuje wyjątek TaskCanceledException.(W dokumentach połączonych: "Jeśli zasób nie zostanie zwrócony w okresie limitu czasu, żądanie wygeneruje wyjątek WebException") – nunofmendes

Odpowiedz

2

Po kilku próbach, manipulowaniu różnymi plikami konfiguracyjnymi, nie znalazłem rozwiązania dotyczącego sposobu na usunięcie limitu czasu wynoszącego 100 sekund.Aby rozwiązać ten konkretny problem, zaimplementowałem środek zaradczy.

Co znalazłem podczas moich prób było:

  • Jeśli projekt jest w WinForms, wszystko działa zgodnie z oczekiwaniami. Oznacza to, że ten 100-sekundowy limit jest "Cechą" UWP;
  • Jeśli zmniejszysz wartość parametru SendTimeout do mniej niż 100 sekund, zostanie wygenerowany wyjątek TimeoutException z odpowiednim zegarem;
  • Nie dotyczy to wyłącznie usługi Windows. Dzieje się tak również podczas komunikacji z implementacją usługi SOAP Webservice;
  • Wydaje się, że dzieje się tak tylko wtedy, gdy wykonujesz zadanie, które wymaga "komunikacji zewnętrznej" z referencją do usługi. Jeśli masz zadanie "wewnętrzne", które trwa dłużej niż 100 sekund, działa zgodnie z oczekiwaniami (np. Poczekaj na Task.delay (250000)).

Jak to rozwiązałem?

Po rozmowie na kanale C# SO, @Squiggle zaproponował podejście odpytywania i to właśnie zaimplementowałem i przetestowałem.

Są to kroki zrobiłem:

  1. I uaktualniony istniejący żądanie (wezwanie (int val)) usług do zaakceptowania kolejny argument, Guid, więc mogłem zidentyfikować zażądać Chciałem zrobić "ankieta";
  2. Utworzono dodatkowe żądanie w usłudze InquireAboutCurrentRequest, które również zaakceptowało parametr GUID, i zwróciło int;
  3. Zaktualizowałem numer referencyjny usługi w aplikacji UWP z nowym żądaniem;
  4. Zadzwoniłem "czekać na połączenie (val, guid)" z próbą catch. Zrobiłem to, ponieważ 90% tych połączeń wraca w mniej niż 30 sekund *;
  5. W haczyku dodałem "If", który sprawdził, czy wyjątek był anulowany, i czy zostałem wywołany "await InquireAboutCurrentRequest (guid)";
  6. Ta metoda w usłudze Windows sprawdza, czy druga operacja została zakończona i śpi co X sekund. Ponieważ całkowity czas pierwszego połączenia może wynosić najwyżej 2 minuty, wystarczy poczekać 20 sekund;
  7. Potem zajmę się odpowiednio wynikiem, ale przynajmniej tym razem wiem, że "czekałem 2 minuty" na odpowiedź.

Istnieją inne możliwe rozwiązania, takie jak gniazda, których nie próbowałem, które mogłyby działać.

* Jeśli wszystkie żądania trwają dłużej niż 100 sekund, sugeruję stosowanie podejścia odpytywania od początku żądań, zamiast czekać na próbę/catch.

+0

To jest zbieranie niepoprawne. Aby "Ankieta" oznaczała wielokrotne pytanie, "Pula" polega na użyciu grupy zasobów, które poszczególne żądania usuwają z grupy, a następnie zwracają zasób do grupy po zakończeniu. Oba słowa są często używane w programowaniu, dlatego ważne jest, aby użyć właściwego. –

+0

Ok. Dziękuję za wyjaśnienie i edycję :) – nunofmendes

0

Nie jestem pewien dokładnie - ale może lepiej użyć Task.WhenAll zamiast czekać?

+0

Nadal zgłasza wyjątek wyjątku Zadanie dla zadania komunikującego się z usługą. – nunofmendes

Powiązane problemy