2008-09-04 5 views
11

私が書きたいコードに例外をスローするためのC#での良い方法はありますが、このようなものです:私は、スレッドBは、定期的に確認することができます知っている指定されたスレッド

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. 
    } 
} 

、スレッドセーフな方法で、スレッドAによってフラグがセットされているかどうかを調べるが、それはコードをより複雑にする。私が使用できるより良い仕組みがありますか?あなたが別のものを見つける必要があることを、スレッドと同類を中止ように、他のメカニズムによってスレッドにスローされる例外との十分な問題がある

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

ことが可能であったならばすごいああ、あなたはそれが可能であっても、単純な加算例外がスローされるで見ることを期待すべきです。 – Dykam

答えて

10

これは、スレッド間の例外をスローし、良いアイデア

This article talks about ruby's timeout library.ではありません。

このようなことがどうやって根本的に壊れているか説明しています。それはルビで壊れているだけではなく、スレッド間で例外をスローするどこにも壊れています。一言で言えば

、何ができる(とない)が起こることはこれです:

スレッドA:

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(); 
} 

あなたの '定期的なチェック' の例は結構です、と実際にスレッド間で例外を投げているわけではありません。
「次回このフラグを見るときに例外をスローする」というフラグを設定しているだけです。このフラグは、「キャッチまたは最終ブロックの途中でスローされる可能性があります。問題。
しかし、もしあなたがそれを行うつもりなら、あなたは単に "exitnow"フラグを設定し、それを使って例外オブジェクトの作成の手間を省くことができます。揮発性のブールはそれのためにうまくいくでしょう。

10

:ここ

は、定期的にチェックするのがより肉付け例ですそれを行う方法。

例外は、処理が例外的に処理できないことを通知するために使用されるメカニズムです。 何か例外的な何かを経験した何かが例外を使用するようにコードを書くことを避けるようにしてください。

他のスレッドは、例外コードによってスローされる可能性があるすべてのケースで、例外の処理方法をほとんど知らないでしょう。

つまり、例外を使用するよりも、スレッドを中止するためのいくつかのメカニズムがあります。

イベントオブジェクトなどを使用して、処理を中止するようにスレッドに指示するのが最適な方法です。

0

私はあなたがこれをやりたい理由を知りたいと思っています。簡単な方法はありません。良い方法ではないからです。おそらくあなたの設計に戻り、最終目標を達成するためのよりクリーンな方法を見つけ出すべきです。

0

私はそれは良い考えではないと思います。 この問題で別の亀裂を起こしてください - 共有データのような他のメカニズムを使ってスレッド間の信号伝達を試みてください。

0

他の人たちと同じように、私はそれほど良いアイデアではありませんが、本当にやりたければ、SynchronizationContextのサブクラスを作成して、ターゲットスレッドにデリゲートを送信して送信することができますそのようなサブクラスが既に存在するため、WinFormsスレッドで作業が完了します)。ターゲットスレッドは、デリゲートを受け取るために、同等のメッセージポンプを実装する必要があります。

0

@Orionエドワーズ

私はfinallyブロックにスローされる例外についてのあなたのポイントを取ります。

しかし、この例外のような例外のアイデアを使用する方法があると思います。

スレッドA:

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

スレッド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(); 
} 

スレッド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 
     } 
    } 

それとも愚かなことでしょうか?別の問題を研究し、私はあなたの質問のことを思い出し、この記事に出くわしたものの

1

オリオンエドワーズが言っていることは、完全に真実ではないということです。「唯一の」方法ではありません。 CER(制約実行領域)を使用して

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

C#で使用すると、スレッド間の例外からあなたのコードを保護し、アトミック操作として、あなたのリソースを解放することができます。このテクニックは、WindowsのネイティブAPIで動作する.NET Frameworkのいくつかのクラスで使用されています。解放されていないハンドルによってメモリリークが発生する可能性があります。

次の例では、確実PrepareConstrainedRegions方法を使用してハンドルを設定する方法を示しhttp://msdn.microsoft.com/en-us/library/system.runtime.compilerservices.runtimehelpers.prepareconstrainedregions.aspx

を参照してください。指定された既存のハンドルに確実にハンドルを設定するには、ネイティブハンドルの割り当てとその後のSafeHandleオブジェクト内のそのハンドルの記録がアトミックであることを確認する必要があります。これらの操作(スレッドの中断やメモリ不足の例外など)間の失敗は、ネイティブハンドルがリークする原因となります。 PrepareConstrainedRegionsメソッドを使用すると、ハンドルがリークしていないことを確認できます。

のように簡単:

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; 
} 
関連する問題