2008-09-04 6 views
11

Der Code, den ich schreiben möchte, ist wie folgt:Gibt es eine gute Methode in C# für eine Ausnahme auf einem bestimmten Thread werfen

void MethodOnThreadA() 
{ 
    for (;;) 
    { 
     // Do stuff 
     if (ErrorConditionMet) 
      ThrowOnThread(threadB, new MyException(...)); 
    } 
} 

void MethodOnThreadB() 
{ 
    try 
    { 
     for (;;) 
     { 
      // Do stuff 
     } 
    } 
    catch (MyException ex) 
    { 
     // Do the right thing for this exception. 
    } 
} 

ich weiß ich kann Thread B in regelmäßigen Abständen überprüfen, in thread sichere Weise haben , um zu sehen, ob ein Flag von Thread A gesetzt wurde, aber das macht den Code komplizierter. Gibt es einen besseren Mechanismus, den ich verwenden kann?

ist hier ein mehr konkretisierte Beispiel in regelmäßigen Abständen überprüft:

Dictionary<Thread, Exception> exceptionDictionary = new Dictionary<Thread, Exception>(); 

void ThrowOnThread(Thread thread, Exception ex) 
{ 
    // the exception passed in is going to be handed off to another thread, 
    // so it needs to be thread safe. 
    lock (exceptionDictionary) 
    { 
     exceptionDictionary[thread] = ex; 
    } 
} 

void ExceptionCheck() 
{ 
    lock (exceptionDictionary) 
    { 
     Exception ex; 
     if (exceptionDictionary.TryGetValue(Thread.CurrentThread, out ex)) 
      throw ex; 
    } 
} 

void MethodOnThreadA() 
{ 
    for (;;) 
    { 
     // Do stuff 
     if (ErrorConditionMet) 
      ThrowOnThread(threadB, new MyException(...)); 
    } 
} 

void MethodOnThreadB() 
{ 
    try 
    { 
     for (;;) 
     { 
      // Do stuff 
      ExceptionCheck(); 
     } 
    } 
    catch (MyException ex) 
    { 
     // Do the right thing for this exception. 
    } 
} 
+0

Oh wow, wenn das möglich war, sollten Sie es möglich, erwarten sogar bei einer einfachen Addition zu sehen, eine Ausnahme ausgelöst werden. – Dykam

Antwort

10

ist keine gute Idee

This article talks about ruby's timeout library. die Ausnahmen über Threads wirft.

Es erklärt, wie so etwas grundsätzlich gebrochen ist. Es ist nicht nur in Ruby gebrochen, es ist irgendwo gebrochen, die Ausnahmen über Threads löst.

Auf den Punkt gebracht, was kann (und wird) geschieht, ist dies:

ThreadA:

At some random time, throw an exception on thread B: 

ThreadB:

try { 
    //do stuff 
} finally { 
    CloseResourceOne(); 
    // ThreadA's exception gets thrown NOW, in the middle 
    // of our finally block and resource two NEVER gets closed. 
    // Obviously this is BAD, and the only way to stop is to NOT throw 
    // exceptions across threads 
    CloseResourceTwo(); 
} 

Ihre 'periodische Überprüfung' Beispiel ist in Ordnung, wie Sie werfen keine Ausnahmen über Threads hinweg.
Sie setzen gerade eine Flagge, die sagt: "Werfen Sie eine Ausnahme, wenn Sie das nächste Mal auf diese Flagge schauen", was gut ist, da es nicht unter dem "kann in der Mitte Ihres Fangs geworfen oder schließlich blockieren" Problem.
Wenn Sie das tun, können Sie aber auch einfach ein "exitnow" -Flag setzen und sich damit die Mühe machen, das Ausnahmeobjekt zu erstellen. Ein flüchtiger Bool wird dafür gut funktionieren.

10

Es gibt genug Probleme mit Ausnahmen, die auf Themen von anderen Mechanismen ausgelöst werden können, wie Fäden und die Gleichen Abbruch, dass Sie einen anderen finden sollten Art und Weise es zu tun.

Eine Ausnahme ist ein Mechanismus, der signalisiert, dass ein Prozess etwas Außergewöhnliches erfahren hat, mit dem er nicht umgehen kann. Sie sollten versuchen, den Code zu schreiben, so dass eine Ausnahme verwendet wird, um zu signalisieren, dass etwas anderes etwas Außergewöhnliches erlebt hat.

Dieser andere Thread wird wahrscheinlich nicht wissen, wie die Ausnahme behandelt in allen Fällen, wo es von Ihrem Code geworfen werden könnte.

Kurz gesagt, Sie sollten einen anderen Mechanismus zum Abbrechen Ihrer Threads finden als die Verwendung von Ausnahmen.

Verwenden Sie Ereignisobjekte oder ähnliches, um einem Thread mitzuteilen, dass er die Verarbeitung abbrechen soll, das ist der beste Weg.

0

Ich bin interessiert zu wissen, warum Sie dies tun möchten. Es gibt keinen einfachen Weg dazu, weil es keine gute Übung ist. Sie sollten wahrscheinlich zu Ihrem Entwurf zurückkehren und einen saubereren Weg finden, um das Endziel zu erreichen.

0

Ich glaube nicht, dass das eine gute Idee ist. Machen Sie einen weiteren Riss bei diesem Problem - Versuchen Sie, einen anderen Mechanismus wie geteilte Daten zu verwenden, um zwischen Threads zu signalisieren.

0

Wie die anderen bin ich mir nicht sicher, ob das so eine gute Idee ist, aber wenn Sie es wirklich tun wollen, dann können Sie eine Unterklasse von SynchronizationContext erstellen, die das Senden und Senden von Delegaten an den Zielthread erlaubt (wenn es ein WinForms thread die Arbeit ist für Sie erledigt, da eine solche Unterklasse bereits existiert). Der Ziel-Thread muss jedoch eine Art Nachrichtenpumpen-Äquivalent implementieren, um die Delegierten zu empfangen. Diese

0

@Orion Edwards

Ich nehme Ihren Hinweis auf eine Ausnahme in den Block schließlich geworfen.

Allerdings denke ich, es gibt einen Weg - mit einem anderen Thread - dieser Ausnahme als Interrupt-Idee zu verwenden.

Thema A:

At some random time, throw an exception on thread C: 

Thema B:

try { 
    Signal thread C that exceptions may be thrown 
    //do stuff, without needing to check exit conditions 
    Signal thread C that exceptions may no longer be thrown 
} 
catch { 
    // exception/interrupt occurred handle... 
} 
finally { 
    // ...and clean up 
    CloseResourceOne(); 
    CloseResourceTwo(); 
} 

Thema C:

while(thread-B-wants-exceptions) { 
     try { 
      Thread.Sleep(1) 
     } 
     catch { 
      // exception was thrown... 
      if Thread B still wants to handle exceptions 
       throw-in-B 
     } 
    } 

ist oder nur dumm?

2

Während ein anderes Problem untersucht, ich auf diesen Artikel kam, die mich auf Ihre Frage erinnert:

Plumbing the Depths of the ThreadAbortException using Rotor

Es zeigt die Verrenkungen, die .NET durch Thread.Abort zu implementieren geht() - vermutlich jeder Eine andere Cross-Thread-Ausnahme müsste ähnlich sein. (Yeech!)

1

Was Orion Edwards sagt, ist nicht ganz richtig: ist nicht der "einzige" Weg.

// Obviously this is BAD, and the only way to stop is to NOT throw 
// exceptions across threads 

CER Verwendung (Constrained Execution Regionen) in C# können Sie Ihre Ressourcen als atomare Operation freizugeben, den Code von Inter-Thread Ausnahmen zu schützen. Diese Technik wird von mehreren .NET Framework-Klassen verwendet, die mit der systemeigenen API von Windows arbeiten, wobei ein nicht freigegebenes Handle möglicherweise zu einem Speicherverlust führt.

Siehe http://msdn.microsoft.com/en-us/library/system.runtime.compilerservices.runtimehelpers.prepareconstrainedregions.aspx

Das folgende Beispiel zeigt, wie Griffe zuverlässig auf die PrepareConstrainedRegions-Methode unter Verwendung. Um ein Handle für ein bestimmtes bereits vorhandenes Handle verlässlich festzulegen, müssen Sie sicherstellen, dass die Zuweisung des systemeigenen Handles und die anschließende Aufzeichnung dieses Handles innerhalb eines SafeHandle Objekts atomar ist. Jeder Fehler zwischen diesen Vorgängen (z. B. ein Thread-Abbruch oder eine Außer-Speicher-Ausnahme) führt dazu, dass das systemeigene Handle verloren geht. Sie können die Methode PrepareConstrainedRegions verwenden, um sicherzustellen, dass der Handle nicht ausgelaufen ist.

So einfach wie:

public MySafeHandle AllocateHandle() 
{ 
    // Allocate SafeHandle first to avoid failure later. 
    MySafeHandle sh = new MySafeHandle(); 

    RuntimeHelpers.PrepareConstrainedRegions(); 
    try { } 
    finally // this finally block is atomic an uninterruptible by inter-thread exceptions 
    { 
     MyStruct myStruct = new MyStruct(); 
     NativeAllocateHandle(ref myStruct); 
     sh.SetHandle(myStruct.m_outputHandle); 
    } 

    return sh; 
} 
Verwandte Themen