1

これは反復的で閉じられているとマークされる可能性があると思いますが、私の人生では、この質問に対する明確で簡潔な答えを見つけることはできません. すべての返信とリソースは、Windows フォームのみを扱い、BackgroundWorker などのビルド済みユーティリティ クラスを利用しています。基本的な知識を他のスレッドの実装に適用できるように、この概念をその核心で理解したいと思っています。

私が達成したいことの簡単な例:

//timer running on a seperate thread and raising events at set intervals
//incomplete, but functional, except for the cross-thread event raising
class Timer
{
    //how often the Alarm event is raised
    float _alarmInterval;
    //stopwatch to keep time
    Stopwatch _stopwatch;
    //this Thread used to repeatedly check for events to raise
    Thread _timerThread;
    //used to pause the timer
    bool _paused;
    //used to determine Alarm event raises
    float _timeOfLastAlarm = 0;

    //this is the event I want to raise on the Main Thread
    public event EventHandler Alarm;

    //Constructor
    public Timer(float alarmInterval)
    {
        _alarmInterval = alarmInterval;
        _stopwatch = new Stopwatch();
        _timerThread = new Thread(new ThreadStart(Initiate));
    }

    //toggles the Timer
    //do I need to marshall this data back and forth as well? or is the
    //_paused boolean in a shared data pool that both threads can access?
    public void Pause()
    {
        _paused = (!_paused);            
    }

    //little Helper to start the Stopwatch and loop over the Main method
    void Initiate()
    {
        _stopwatch.Start();
        while (true) Main();    
    }

    //checks for Alarm events
    void Main()
    {
        if (_paused && _stopwatch.IsRunning) _stopwatch.Stop();
        if (!_paused && !_stopwatch.IsRunning) _stopwatch.Start();
        if (_stopwatch.Elapsed.TotalSeconds > _timeOfLastAlarm)
        {
            _timeOfLastAlarm = _stopwatch.Elapsed.Seconds;
            RaiseAlarm();
        }
    }
}

ここに 2 つの質問があります。主に、アラーム イベントの関係者に警告するためにイベントをメイン スレッドに取得する方法を教えてください。

次に、メインスレッドで実行されているオブジェクトによって呼び出される Pause() メソッドについて。_stopwatch.start()/_stopwatch.stop() を呼び出して、バックグラウンド スレッドで作成されたストップウォッチを直接操作できますか? そうでない場合、バックグラウンド スレッドが _paused の新しい値を確認して使用できるように、メイン スレッドは上記のように _paused ブール値を調整できますか?

断言しますが、私は調査を行いましたが、これらの (基本的かつ重要な) 詳細はまだ明らかになっていません。

免責事項: Timer クラスで説明している特定の機能を正確に提供するクラスが利用可能であることは承知しています。(実際、このクラスは単に Threading.Timer と呼ばれていると思います) ただし、私の質問は、Timer クラス自体の実装を支援するものではなく、それを駆動する概念を実行する方法を理解するためのものではありません。

4

2 に答える 2

1

既存のスレッドで魔法のようにコードを実行することはできません。
代わりに、スレッドセーフなデータ構造を使用してコードを明示的に実行する既存のスレッドが必要です。

これがどのように機能するかControl.Invokeです(これがどのように機能するかBackgroundWorkerです)。
WiinForms は、Application.Run()おおよそ次のようなメッセージ ループを実行します。

while(true) {
    var message = GetMessage();  //Windows API call
    ProcessMessage(message);
}

Control.Invoke()デリゲートを実行するように伝える Windows メッセージを (Windows 内のスレッドセーフなメッセージ パッシング コードを使用して) 送信します。 ProcessMessage(UI スレッドで実行されます) はそのメッセージをキャッチし、デリゲートを実行します。

これを自分で行いたい場合は、独自のループを作成する必要があります。これには、.Net 4.0 の新しいスレッド セーフな Producer-Consumer コレクションを使用するか、デリゲート フィールド ( with Interlocked.CompareExchange) とを使用AutoResetEventして自分で行うことができます。

于 2012-06-27T16:35:03.267 に答える
1

注:コメントに十分なスペースがないため、ここにこれを書いています。これはもちろん完全ではなく、完全な答えでもありません。

私は常にイベントを使用して、関係のないコードに何かを実行するように通知していたので、それが私の意図を説明した方法です。マーシャリングとイベントの違いと、別のタイプのデータ (シグナル) のマーシャリングの違いがよくわかりません。

概念的には、どちらもイベントとして扱うことができます。提供された同期/シグナリング オブジェクトを使用することと、このようなものを自分で実装しようとすることの違いは、誰がどのように仕事を成し遂げるかです。

.net のイベントは単なるデリゲートであり、イベントのプロバイダーがイベントを起動したときに実行する必要があるメソッドへのポインターのリストです。あなたが話していること(イベントのマーシャリング)は、あなたが正しく理解している場合、何かが起こったときにイベントオブジェクトを共有することですが、signaligの概念は通常、最初に共有されるオブジェクトについて話し、両方のスレッドが何かが起こったことを「知っています」その状態を手動または自動でチェックします (.net と Windows の両方が提供するツールに依存します)。

最も基本的なシナリオでは、boolean変数を使用してこのようなシグナリングの概念を実装できます。booleanは true であり、何かが発生したことを通知する方法として、そのような別の設定を行います。.NET によって提供されるさまざまなシグナル伝達ツールは、シグナルがない (ブール値が false に等しい) 限り、待機中のスレッドを実行しないことによって、リソースの浪費が少ない方法でこれを行いますが、概念的には同じ考えです。

于 2012-06-27T17:47:21.017 に答える