2009-03-27 10 views
5

Come parte di un processo di automazione di grandi dimensioni, stiamo chiamando un'API di terze parti che esegue alcuni servizi di chiamata di lavoro su un'altra macchina. Recentemente abbiamo scoperto che ogni tanto quando l'altra macchina non è disponibile, la chiamata API verrà eseguita a volte fino a 40 minuti durante il tentativo di connessione al server remoto.Un uso ragionevole del threading in C#?

L'API che stiamo utilizzando non offre un modo per specificare un timeout e non vogliamo che il nostro programma sia in attesa da così tanto tempo, quindi ho pensato che i thread sarebbero stati un buon modo per forzare il timeout. Il codice risultante è qualcosa di simile:

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

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

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

Fondamentalmente, voglio lasciare apicall run(), ma se si è ancora in corso dopo il timeout è trascorso, scontato che sta per fallire, ucciderlo e andare avanti.

Dal momento che sono nuovi per la filettatura in C# e il runtime .NET ho pensato di chiedere due questioni correlate:

c'è una migliore/meccanismo più appropriato nelle librerie .NET per quello che ho' sto cercando di fare e ho commesso qualche thread di threading in quel pezzetto di codice?

risposta

5

Thread.Abort() è una richiesta di interruzione del thread e non garantisce che lo farà in modo tempestivo. È anche considerato una cattiva pratica (genererà un'eccezione di interruzione del thread nel thread interrotto, ma sembra che l'API di terze parti non offra altre scelte.

Se si conosce (a livello di codice) l'indirizzo dell'host del servizio remoto si dovrebbe rumore metallico prima di trasferire il controllo al 3 ° API partito.

Se non si utilizza un BackgroundWorker, è possibile impostare IsBackgroundThread del thread su true, in modo da non mantenere il vostro programma di terminare.

3

Cattiva idea. Thread.Abort non elimina necessariamente il disordine lasciato da una chiamata API interrotta.

Se la chiamata è costosa, prendere in considerazione la scrittura di un exe separato che effettua la chiamata e passare gli argomenti a/da esso utilizzando la riga di comando oi file temporanei. Puoi uccidere un .exe in modo molto più sicuro che uccidere un thread.

0

potrebbe lavoro, ma nessuno potrebbe dire per certo senza una comprensione dell'API di terze parti. Annullare il thread in questo modo potrebbe lasciare il componente in uno stato non valido che potrebbe non essere e per recuperare da, o forse non libererà risorse che ha allocato (pensa - cosa succede se una delle tue routine ha appena smesso di funzionare a metà. Potresti fornire garanzie circa lo stato in cui si troverebbe il tuo programma?).

Come suggerito da Cicil, potrebbe essere una buona idea eseguire prima il ping del server.

3

È anche possibile utilizzare un delegato ... Creare un delegato per il metodo che esegue il lavoro, quindi chiamare BeginInvoke sul delegato, passandogli gli argomenti e una funzione di callback per gestire i valori restituiti (se si desidera) ... Subito dopo il BeginInvoke si può aspettare un tempo designato per il delegato asincrono per finire, e se non lo fa in quel periodo di tempo specificato, andare avanti ...

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 
    } 

NOTA: come scritto AsynchCallback poteva essere nullo, in quanto il codice recupera il valore di ritorno da EndInvoke(), ma se lo desideri puoi avere il metodo CallService() chiama il delegato AsynchCallback e passa i valori di ritorno instati ...

+0

Come si gestisce il timeout? Lasciate che il componente continui a provare, per un massimo di 40 minuti, a connettersi al server mancante? Penso che la domanda riguardasse come impedire al componente di provare a connettersi. – Cybis

+0

Siamo spiacenti, è stato modificato per aggiungere codice di esempio ... la domanda di timeout è indirizzata da DateTime.Ora

0

L'applicazione viene eseguita per lunghi periodi di tempo o è un'applicazione più necessaria? Se è il secondo, personalmente prenderei in considerazione l'utilizzo dell'opzione Thread.Abort(). Anche se potrebbe non essere il più desiderabile dal punto di vista di un purista (gestione delle risorse, ecc.), È certamente semplice da implementare e può pagare il conto dato il modo in cui funziona la tua particolare applicazione.

L'idea di un eseguibile separato ha senso. Forse un'altra opzione potrebbe essere l'utilizzo di AppDomain. Non sono un esperto in questo settore (accetto i miglioramenti/correzioni a questo), ma, a quanto ho capito, avresti messo la chiamata API in una DLL separata e caricandola in un AppDomain separato. Quando la chiamata API è terminata o devi annullarla, puoi scaricare AppDomain insieme alla DLL. Questo può ha il vantaggio aggiunto di ripulire le risorse che non sarà un Thread.Abort() diretto.