Home > c# > Should I use: One event multiple subscribers or multiple events multiple subscribers

Should I use: One event multiple subscribers or multiple events multiple subscribers

May 9Hits:0
Advertisement

I am writing a data acquisition application. I wondering whether I should use one event multiple subscribers or multiple events multiple subscribers. I am worried about performance. Also could I improve performance by offloading the subscribers to a different thread?

Here is an example of what I mean by multiple events Multiple subscribers.

public delegate void EventChannelUpdated(object sender, EventArgsChannelUpdated e);  public class EventArgsChannelUpdated : EventArgs {     public chCell chCell; }  public delegate void EventLimitExceeded(object sender, EventArgsLimitExceeded args);  public class EventArgsLimitExceeded : EventArgs { }  class Example {     public List<Channel> AllChannels; }  public class Saver_allChannels {     private Example example;      public event EventChannelUpdated eventChannelUpdated;      public void DataAdded()     {          //** this part is pseudo code as I have simplified some of the hwObjs structure         for (int i = 0; i < hwObjs.Count; i++)         {             example.ListOfchannels[i].addSamples(hwObjs[i].Reading);         }     } }  public class Channel  {     public List<chCell> column { get; set; }      public string label { get; set; }      public event EventChannelUpdated eventChannelUpdated;      public double multipler { get; set; }      public void addSamples(double unScaledValue)     {         chCell temp = new chCell() {channel = this, scaledValue = unScaledValue*multipler};         column.Add(temp);         eventChannelUpdated(this,new EventArgsChannelUpdated(){chCell = temp});     } }  public class chCell {     public double scaledValue { get; set; }      public Channel channel { get; set; } //** parent Channel List }  public class Monitor {     public double Limit;      public event EventLimitExceeded eventLimitExceed;      public void Handle_EventChannelUpdated(object sender, EventArgsChannelUpdated args)     {         if (args.chCell.scaledValue > Limit)         {             eventLimitExceed(this, new EventArgsLimitExceeded());         }     } } 

Here is an example of what I mean by one event multiple subscribers

public delegate void EventChannelUpdated(object sender, EventArgsChannelsUpdated e);  public class EventArgsChannelsUpdated : EventArgs {     public List<chCell> RowOfchCells; }  public delegate void EventLimitExceeded(object sender, EventArgsLimitExceeded args);  public class EventArgsLimitExceeded : EventArgs { }  class Example {     public List<Channel> ListOfchannels; }   public class Saver_allChannels {     Example example;      public event EventChannelUpdated eventChannelUpdated;      public void DataAdded()     {          List<chCell> LastRow = new List<chCell>();          //** this part is pseudo code as I have simplified some of the hwObjs structure         for (int i = 0; i < hwObjs.Count; i++)         {                            example.ListOfchannels[i].addSamples(hwObjs[i].Reading);             LastRow.Add(new chCell() {channel = example.ListOfchannels[i], Value = hwObjs[i].Reading});         }          eventChannelUpdated(this, new EventArgsChannelsUpdated() { RowOfchCells = LastRow });      } }  public class Channel  {     public List<chCell> column { get; set; }      public string label { get; set; }      public double multipler { get; set; }      public void addSamples(double unScaledValue)     {         chCell temp = new chCell() {channel = this, scaledValue = unScaledValue*multipler};         column.Add(temp);     } }  public class chCell {     public double scaledValue { get; set; }      public Channel channel { get; set; } //** parent Channel List }  public class Monitor {     public double Limit;      public Channel subbedChannel;      public event EventLimitExceeded eventLimitExceed;      public void Handle_EventChannelUpdated(object sender, EventArgsChannelsUpdated args)     {         double value = (args.RowOfchCells.FirstOrDefault(x => x.channel == subbedChannel)).scaledValue;         //** do something with value     } } 

Answers

If you get these items in batches, then it obviously makes sense to use events that notify about changes once per batch:

public class AllChannelsSaver
{
    public void Add(IEnumerable<ChCell> cells)
    {
        this.m_cells.Add(cells);

        this.OnCellsAdded(cells);
    }

    public event EventHandler<MultipleCellsAddedEventArgs> CellsAdded;

    private void OnCellsAdded(IEnumerable<ChCell> cells)
    {
        var cellsAddedDelegate = this.CellsAdded;

        if (cellsAddedDelegate == null)
            return;

        cellsAddedDelegate(this, new MultipleCellsAddedEventArgs(cells));
    }
}

Taking into account that you operate over collections it even makes sense to consider standard INotifyCollectionChanged interface with its CollectionChanged event, depending on whether it suits the domain logic or a more appropriately named event will fit better.


But it is also important to think about two things:

  1. Who is (or should be) responsible for adding items and notifying? Is it all channels object, or is it an individual Channel who is responsible? Do all channels get all cells, or each individual channel gets specific cell?
  2. Ease of use. Your second example's Handle_EventChannelUpdated uses search over the items to get the required item by some condition. If it is not just some example, but a usual usage scenario, then it is obviously something that needs to be reconsidered. Searching each time for a single item, that can be obtained directly is not the best architecture.

If you are not sure what is better from these two points of view, then you can just try to cater to both scenarios - use events on both the global AllChannels aggregator and the individual channels. Just try not to handle the same item twice (perhaps, use some additional Handled flag in EventArgs if it is possible).


Offloading subscribers to different threads

You can do it. The most simple and less error-prone way is to use TPL with Parallel.ForEach over the delegates you get from Delegate.GetInvocationList call:

public static void Execute(object sender, EventArgs args, Int32 number, Int32 delayMs)
{
    Console.WriteLine("Entering {0}", number);

    Console.WriteLine("Sender = {0}; EventArgs = {1}",
        sender,
        args);

    Thread.Sleep(delayMs);

    Console.WriteLine("Exiting {0}", number);
}

public static void Main()
{
    var eventHandler = new EventHandler((sender, args) =>
        Execute(sender, args, 0, 1000));

    eventHandler += (sender, args) =>
        Execute(sender, args, 1, 1000);

    eventHandler += (sender, args) =>
        Execute(sender, args, 2, 2000);

    var senderArg = typeof(Program);
    var eventArgs = new EventArgs();

    Parallel
        .ForEach(
            eventHandler
                .GetInvocationList()
                .Cast<EventHandler>(),
            (handler) =>
                handler(senderArg, eventArgs));

    Console.WriteLine("All is done. Press any key.");
    Console.ReadKey(false);
}

Just make sure that your handlers are not(or make them so) dependent on being raised on the same thread (like handlers modifying UI).

It is also a possible idea to reconsider responsibilies once more - make handlers responsible for offloading. So the handlers themselves offload long-performing job to other threads. But you will have to be very careful, because it will probably violate the invariant that after raising the event all changes have been already handled(because some thread may still continue to process them even after OnEvent returns).

P.S.: While it is not directly related to your question, I'd like to add some points:

  1. Try not to create your specific new delegate classes. While they may add some degree of type-safety, in most cases it is better to use some of the standard generic delegate classes like EventHandler or Action<...> with Func<...>.
  2. I do not want to look like someone who forces you to use a preferred naming convention, but it is still a good idea for you to look into C# naming guidelines on MSDN. Most languages have some more or less widespread, or even standardized way of naming. And such conventions(when you become accustomed to them) can make reading and sharing of programs much easier.

Related Articles

Copyright (C) 2017 ceus-now.com, All Rights Reserved. webmaster#ceus-now.com 14 q. 0.827 s.