2

次のコードでは、2つのクラスがあります。1つは別のスレッドで実行されてイベントを発生させ、もう1つはこのイベントをサブスクライブしてイベントからデータを受信します。JonSkeetの記事http://csharpindepth.com/Articles/Chapter2/Events.aspxに基づいたイベントコード

この記事ではhttp://www.codeproject.com/Articles/37474/Threadsafe-それが言うイベント...

このため、JonSkeetがDelegatesandEventsの最後に推奨するのと同じアプローチをお勧めします。「それをしないでください」、つまり、マルチスレッド方式でイベントを使用しないでください。オブジェクトにイベントが存在する場合、そのイベントをサブスクライブまたはサブスクライブ解除できるスレッドは1つだけであり、イベントを発生させるのは同じスレッドです。

これで明らかに私のデザインはそれを破り、サブスクライブされたスレッドとは異なるスレッドでイベントを発生させます。マルチスレッド方式でイベントを使用しないという原則に準拠するように設計を変更するにはどうすればよいですか、またはこれは不可能ですか?

私がそれを行うことを考えたもう1つの方法は、コールバックメソッドをデリゲートとしてクラスBに渡し、イベントを呼び出す代わりにそれを呼び出すことでしたか?

スティックの端が完全に間違っている可能性があるので、説明をいただければ幸いです。

注:.Net 4.0でこの問題が明らかに解決されたことは承知していますが、.Net4より前にそれを行う方法があるかどうかについては引き続き関心があります。

public delegate void MyDelegate(int a);

class A
{
    void main()
    {
        B bObject = new B();
        bObject.MyEvent += new MyDelegate(NiceMethod);
        bObject.Run();
    }   

    void NiceMethod(int a)
    {   
        Console.Writeline({0}, a);  
    }
}

class B
{
    readonly object eventLock = new object();

    MyDelegate myDel;

    public event MyDelegate MyEvent
    {   
        add
        {
            lock (eventLock)
            {
                myDel += value;
            }
        }
        remove
        {
            lock (eventLock)
            {
                myDel -= value;
            }
        }
    }   

    //Assume this runs in a new thread and calls back data using MyEvent
    //Have ommited thread code for simplicity
    public void Run()
    {
        for (int i = 0; i < 100; i++)
        {
            Thread.Sleep(1000);
            MyDelegate handler;
            lock (someEventLock)
            {
                handler = myDel;
            }
            if (handler != null)
            {
                handler (i);
            }
        }   
    }
}
4

1 に答える 1

1

イベントを発生させたり、さまざまなスレッドからのイベントをリッスンしたりすることに何の問題もありません。別のスレッドからの呼び出しに対処するのはリスナーの責任です。Marc Gravellがコメントで述べているように、異なるスレッドからのイベントへのリスナーの追加とイベントからのリスナーの削除は、生成されたコンパイラーaddremove実装によってサポートされています(そして常にサポートされています)。add唯一の問題は、スレッドセーフな方法でイベントを発生させることです。これは、生成されremoveて使用されているのと同じ種類のスピンロックを介してイベントへのアクセスを同期することで実行できます。

class B 
{  
    public event MyDelegate MyEvent;

    protected OnMyEvent(int p_Arg)
    {
        // Delegates are immutable and add/remove default
        // implementations always generate a new instance of the 
        // delegate. Therefore, tTmp (if not null) can be safely invoked
        var tTmp = 
            System.Threading.Interlocked
            .CompareExchange(ref MyEvent, null, null);
        if (tTmp != null) {
            tTmp(p_Arg);
        }
    }

    //Assume this runs in a new thread and calls back data using MyEvent 
    //Have ommited thread code for simplicity 
    public void Run() 
    { 
        for (int i = 0; i < 100; i++) 
        { 
            OnMyEvent(i);
        }    
    } 
} 

発生する可能性がある唯一のことは、リスナーがイベントリストから削除された後に呼び出されることです。私見、リスナーは別々のスレッドから呼び出された蜂を扱うので、この状況に対処できなければなりません...

于 2012-07-29T10:40:17.407 に答える