4

可能であれば2つの問題を回避するC#のスレッドセーフコードを探しています。

  1. スレッドオブジェクトがいずれかの時点で破棄されると、スレッドループは適切に終了します。
  2. 別のクラスまたはフォーム(特にフォーム)で使用される場合、クラスを使用する開発者は、フォームのcloseイベントをフックしてdisposeを呼び出すことを覚えておく必要はありません。例:サーバーのスレッドの正しい仕上げ

スレッドは、次のwhileループを持つメソッドを呼び出しています。

while (!Finished)
{
    string request = "";
    try
    {
        //Block until we have a message or we are terminated in dispose **
        request = server.Recv(Encoding.Unicode);  
        //Send a response to the sender so they know we received. 
        server.Send(request, Encoding.Unicode);
    }
    catch (Exception e)
    {
        //Catch a termination error. 
        if (e.Errno == ETERM)
        {
            break;
        }
    }
    syncContext.Post(
                     new SendOrPostCallback(delegate(object state)
                     {
                         MessageHandler handler = OnMessage;
                         if (handler != null)
                         {
                             handler(request);
                         }
                     }), null);
}

そして、処分は現在そのように見えます。

public void Dispose()
{
    Finished = true;
    //Cause all blocking message requests and sends to terminate with exception ** 
    FZMQConext.Dispose();

    //Wait for the thread to finish up.
    FThread.Join();
    GC.SuppressFinalize(this);
}

私が見ている問題は次のとおりです。

  • スレッドのロールアウトを終了する必要があるため、クラスを破棄しているにもかかわらず、イベントが呼び出される可能性があります。破棄されたと言うのは、try / catchとsyncContext.Post()の間で呼び出されます。これはどれくらい悪いですか?
  • このクラスをまとめて、ユーザーがdisposeを呼び出すことを覚えておく必要がないようにする方法はありますか。現在、彼らがいない場合、アプリケーションはスレッドが終了するのを待ってそこに座っていますが、それは決してありません。バックグラウンドスレッドに設定するのは怠惰なようです。
4

1 に答える 1

1

スレッドのロールアウトを終了する必要があるため、クラスを破棄しているにもかかわらず、イベントが呼び出される可能性があります。破棄されたと言うのは、try / catchとsyncContext.Post()の間で呼び出されます。これはどれくらい悪いですか?

それほど悪くはないようです。設定したらFinished=true。新しいデリゲートの投稿を終了してから、ループを終了する必要があります。ただし、でCPUキャッシュの問題が発生する可能性がありますFinished。ループを実行しているCPUコアは、別のスレッドが値を変更した後にキャッシュされた値を読み取る可能性があるため、ループが誤って実行を継続する可能性があります。これを防ぐには、を介してのみ変更されるプライベートフィールドと、そのフィールドを変更するメソッドを作成する必要があります。Finished Interlocked

private int _running = 1;
public void Stop()
{
    Interlocked.Exchange(ref _running, 0);
}

ループが実行されますwhile( _running > 0)

このクラスをまとめて、ユーザーがdisposeを呼び出すことを覚えておく必要がないようにする方法はありますか。現在、彼らがいない場合、アプリケーションはスレッドが終了するのを待ってそこに座っていますが、それは決してありません。バックグラウンドスレッドに設定するのは怠惰なようです。

クライアントに処分の負担をかけることを避ける一般的な方法は実際にはありません。ただし、このクラスが常にWinformsコンポーネントによって使用されることを意図している場合は、親コンポーネントを取得してDisposed親のイベントをサブスクライブするコンストラクターを作成できます。したがって、親コンポーネント(フォームなど)が破棄されると、このオブジェクトも破棄されます。

public MyClass(Component parent)
{
    parent.Disposed += (s,e) => this.Dispose();
}

(ファイナライザーが実行されるかどうか/いつ実行されるかわからないため、オプションとしてファイナライザーを破棄します。)

于 2012-09-07T08:34:44.447 に答える