2010-02-15 8 views
34

Ich habe Objekt obj die 3rd-Party-Komponente ist,Stellen Timeout auf eine Operation

// this could take more than 30 seconds 
int result = obj.PerformInitTransaction(); 

Ich weiß nicht, was im Inneren geschieht. Was ich weiß ist, wenn es länger dauert, ist es fehlgeschlagen.

Wie ein Timeout-Mechanismus für diese Operation einrichten, so dass, wenn es mehr als 30 Sekunden dauert, ich einfach MoreThan30SecondsException werfen?

Antwort

66

Sie könnten den Betrieb in einem separaten Thread ausführen und dann auf den Thread ein Timeout setzen Join-Operation:

using System.Threading; 

class Program { 
    static void DoSomething() { 
     try { 
      // your call here... 
      obj.PerformInitTransaction();   
     } catch (ThreadAbortException) { 
      // cleanup code, if needed... 
     } 
    } 

    public static void Main(params string[] args) { 

     Thread t = new Thread(DoSomething); 
     t.Start(); 
     if (!t.Join(TimeSpan.FromSeconds(30))) { 
      t.Abort(); 
      throw new Exception("More than 30 secs."); 
     } 
    } 
} 
+1

@Bomboca: Ich rollte deine Bearbeitung zurück, die 'Exception' Ich werfe nicht ein' ThreadAbortException', die etwas von der CLR geworfen ist sollte ein Anruf, wenn zu "Abort" wird gemacht. –

+0

Entschuldigung dafür und danke für die Eingabe :) –

+2

Es ist ein blockierender Anruf, wenn Sie den Haupt-Thread andere Dinge zu dieser Zeit brauchen, wird dies nicht funktionieren! – feldeOne

1

Sie bei Aufruf der Methode in einem Thread aussehen könnte und auf das Timeout, brechen Sie den Faden und erhebe die Ausnahme. Außerdem müssen Sie in diesem Fall die ThreadBorted-Exception behandeln.

+0

Sie haben Recht mit der ThreadAbortedException, +1. –

0

Es gibt ein schönes Beispiel für eine generische Lösung mit einer Hilfsklasse here.

Er verwendet den Aktionsdelegaten, um die im vorherigen Beispiel gezeigte Erstellung/Löschung von Threads zu vermeiden.

Ich hoffe, das hilft.

2

Sie müssen vorsichtig sein, wenn Sie einen Vorgang wie diesen abbrechen, besonders da er in einer Drittanbieterkomponente enthalten ist, auf die Sie (möglicherweise) keinen Zugriff auf den zu ändernden Code haben.

Wenn Sie den Vorgang abbrechen, wissen Sie nicht, in welchem ​​Zustand Sie die zugrunde liegende Klasse verlassen haben. Zum Beispiel hat er möglicherweise eine Sperre erhalten, und Ihr About hat dazu geführt, dass diese Sperre nicht aufgehoben wird. Selbst wenn Sie das Objekt nach dem Abbruch der Operation zerstören, hat es möglicherweise einen Status geändert, der global ist, und Sie werden daher nicht in der Lage sein, eine neue Instanz ohne Neustart neu zu erstellen.

6

Wenn Sie nicht möchten, dass der Haupt-Thread blockieren Sie eine System.Threading.Timer verwenden:

private Thread _thread; 

void Main(string[] args) 
{ 
    _thread = new ThreadStart(ThreadEntry); 
    _thread.Start(); 
    Timer timer = new Timer(Timeout,null,30000,Timeout.Infinite); 
} 


void ThreadEntry() 
{ 
    int result = obj.PerformInitTransaction(); 
} 

void TimeOut(object state) 
{ 
    // Abort the thread - see the comments 
    _thread.Abort(); 

    throw new ItTimedOutException(); 
} 

Jon Skeet hat eine weniger gewaltsame Weise (Shutting Down Worker Threads Gracefully) des Anhaltens der Faden als zunichte machen.

Da Sie jedoch nicht die Kontrolle über die Operationen PerformInitTransaction() haben, gibt es nicht viel, was Sie tun können, wenn der Abbruch fehlschlägt und das Objekt in einem ungültigen Zustand belässt. Wie bereits erwähnt, wenn Sie in der Lage sind, alles zu bereinigen, das die PerformInitTransaction abgebrochen hat, können Sie dies tun, indem Sie die ThreadAbortException fangen, obwohl es ein Anruf von Drittanbietern ist, bedeutet es den Zustand zu erraten, in dem Sie ihre Methode verlassen haben.

Die PerformInitTransaction sollte wirklich diejenige sein, die das Timeout liefert.

10

Mehr einfach mit Task.Wait(TimeSpan):

using System.Threading.Tasks; 

var task = Task.Run(() => obj.PerformInitTransaction()); 
if (task.Wait(TimeSpan.FromSeconds(30))) 
    return task.Result; 
else 
    throw new Exception("Timed out"); 
+0

Dies ist sehr einfach und seit .net 4.0 verfügbar. –

+0

Das wird den Thread jedoch nicht beenden –