3

主な問題は、1つのスレッドからイベントを発生させると、特定のスレッドコンテキストでのみ呼び出されるデリゲートを呼び出すことができることです。私が考えたこの問題についていくつかの調査を行った後、イベントへの各サブスクリプションとともに、ある種の同期コンテキストを渡すことが可能かもしれません。

SomeClass.SmartSyncEvent += (myDelegate, someReferenceToAThread);

そして、イベントを発生させ、それはどういうわけか:

foreach(subscriber)
{
    someReferenceToAThread.Invoke(myDelegate);
}

これは超擬似コードですが、誰かがすでにこのようなことをしたか、そのようなパターンを設定できる.NETクラスを知っているかもしれません。ありがとう!

4

2 に答える 2

7

これを行う最良の方法はSomeClass、を介して同期コンテキストを渡すことISynchronizeInvokeです。

public class SomeClass
{
    public event EventHandler SmartSyncEvent;

    public ISynchronizeInvoke SynchronizingObject { get; set; }

    public void RaiseSmartSyncEvent()
    {
        if (SynchronizingObject != null)
        {
            SynchronizingObject.Invoke(
              (Action)(()=>
              {
                  SmartSyncEvent();
              }), null);
        }
        else
        {
            SmartSyncEvent();
        }
    }
}

System.Timers.Timerこのパターンは、実装方法に似ています。これに伴う問題は、すべてのサブスクライバーが同じ同期オブジェクトにマーシャリングされることです。しかし、これがあなたが望むものではないようです。

幸いなことに、デリゲートは、プロパティを介してメソッドを呼び出す必要があるクラスインスタンスを格納しTargetます。デリゲートを呼び出す前に抽出し、もちろんそれ自体であると想定し同期オブジェクトとして使用することで、これを悪用できISynchronizeInvokeます。カスタムの追加および削除イベントアクセサーを使用することで、実際にそれを実施できます。

public class SomeClass
{
    private EventHandler _SmartSyncEvent;

    public event EventHandler SmartSyncEvent
    {
        add
        {
            if (!(value.Target is ISynchronizeInvoke))
            {
                throw new ArgumentException();
            }
            _SmartSyncEvent = (EventHandler)Delegate.Combine(_SmartSyncEvent, value);
        }
        remove
        {
            _SmartSyncEvent = (EventHandler)Delegate.Remove(_SmartSyncEvent, value);
        }
    }

    public void RaiseMyEvent()
    {
        foreach (EventHandler handler in _SmartSyncEvent.GetInvocationList())
        {
            var capture = handler;
            var synchronizingObject = (ISynchronizeInvoke)handler.Target;
            synchronizingObject.Invoke(
                (Action)(() =>
                {
                    capture(this, new EventArgs());
                }), null);
        }
    }
}

これは、各サブスクライバーを他のサブスクライバーから独立してマーシャリングできるという点ではるかに優れています。それに関する問題は、ハンドラーがISynchronizeInvokeクラスに存在するインスタンスメソッドでなければならないということです。また、Delegate.Target静的メソッドの場合はnullです。addこれが、カスタムアクセサーでその制約を適用する理由です。


Delegate.Targetnullの場合、または有用な同期オブジェクトにキャストできない場合は、ハンドラーを同期的に実行させることができます。このテーマにはたくさんのバリエーションがあります。

DispatcherObjectWPFでは、の代わりにをコーディングできますISynchronizeInvoke

于 2012-05-06T01:57:51.790 に答える
1

このトピックの私の答えを参照してください。私が書いた3つのブログ投稿とYouTubeの説明ビデオへのリンクを提供します。これらはすべて、UIとの同期についてスレッドセーフに説明しています。それぞれのブログ投稿は、あなたが求めていることを実行するための完全に異なる方法をカバーしているので、どの方法を使用するかはあなた次第です。いくつかは他のものより簡単ですが、すべて一般的に中間です。

リンクされた質問から引用したものを編集します。

実際にはたくさんのオプションがあります。

(1)BackgroundWorker。WinFormsでの非同期作業のための最も簡単なプログラミングモデルが本当に必要な場合は、これになります。通常、非同期タスクを実行して進行状況を報告するために使用されますが、必要がない場合は進行状況を報告する必要はありません。

(2)イベントベースの非同期パターン。非同期タスクを実行する本格的なコンポーネントを作成し、レポートの進行状況と独自のカスタムイベントを完全に制御する場合は、これが1つの方法です。これは、BackgroundWorkerよりもスレッド化を理解するのにも役立ちます。私は視覚的な人間なので、WinFormsを使用してこれを行う方法についての完全なビデオプレゼンテーションを作成しました

(3)タスク並列ライブラリ。WinFormsでTPLを使用できます。これを行う方法について、非常に詳細なブログ投稿をここに書きました。

(4)AsyncandAwait。これには、.NET 4.5、C#5.0、およびC#5.0コンパイラがVisual Studio 11にのみ含まれている必要があることに注意してください。これは、現在ベータ版のみです。ただし、これを行う方法についての完全なブログ投稿もあります

(5)スレッドを使用したISynchronizedInvoke。これは別のオプションであり、私もについての完全なブログを持っています。

どの方法を選択するかは、実際にはあなた次第です。私の提案は、それぞれを簡単に見て、対象があなたにどれだけ進んでいるか、またはどの方法があなたの要件を最もよく満たす可能性があるかに基づいて方法を選ぶことです。

于 2012-05-06T01:21:36.130 に答える