0

I have the following loop that notifies a list of observers of a certain event:

 foreach (var observer in registeredObservers)
{
    if (observer != null)
    {
        observer.OnMessageRecieveEvent(new ObserverEvent(item));
    }
}

Is there A way I can use the TPL to possibly notify all the registered Observers at once?

Here is the code that is Executed in the OnMessageRecieveEvent()

 public void OnMessageRecieveEvent(ObserverEvent e)
    {
        SendSignal(e.message.payload);
    }

 private void SendSignal(Byte[] signal)
    {
        if (state.WorkSocket.Connected)
        {
            try
            {
                // Sends async
                state.WorkSocket.BeginSend(signal, 0, signal.Length, 0, new AsyncCallback(SendCallback), state.WorkSocket);
            }
            catch (Exception e)
            {                    
                log.Error("Transmission Failier for ip: " + state.WorkSocket.AddressFamily , e);
            }
        }
        else
        {
            CloseConnection();
        }
    }

So my questions is:

  1. How can I do this:

  2. Do I actually want to do this? Would it be beneficial to performance?

svick
  • 236,525
  • 50
  • 385
  • 514
Zapnologica
  • 22,170
  • 44
  • 158
  • 253
  • 2
    use [Parallel.ForEach](http://msdn.microsoft.com/en-us/library/system.threading.tasks.parallel.foreach(v=vs.110).aspx) – nraina Jul 17 '14 at 10:20
  • It depends on how you measure performance. Certainly using something like the TPL to notify each observer in parallel would improve throughput. However, ultimately the overall speed in which notifications are received are dependant on your socket technology & hardware. What problems are you seeing at the moment? – James Jul 17 '14 at 10:32
  • Try measuring a loop based on `foreach` and one based on `Parallel.ForEach` – Yuval Itzchakov Jul 17 '14 at 10:39
  • Any once care to give an example on how to convert it to Parallel.ForEach – Zapnologica Jul 17 '14 at 11:01
  • @Zapnologica there are tons in the documentation. – James Jul 17 '14 at 11:04
  • Your question is: How to process work items in parallel with the TPL? This has been discussed a lot already. You need a very basic tutorial. – usr Jul 17 '14 at 11:15

3 Answers3

3

Since all a single iteration of your loop does is to start an asynchronous socket operation (which is very fast by itself), you most likely wouldn't benefit from parallelizing your code.

svick
  • 236,525
  • 50
  • 385
  • 514
1

Your foreach loop written as Parallel.ForEach. Approximately.

Parallel.ForEach(registeredObservers, (obs, item) =>
        {
            if (obs != null) 
               obs.OnMessageReceivedEvent(new ObserverEvent(item));
        });
Patrick Allwood
  • 1,822
  • 17
  • 21
  • This is Ideal thanks, However what will happen if the obs. fucntion is send data async on a socket? Will this not just offload the entire buffer into the async send buffer? Would it not be better to maybe no a synchronous network send and then do a task.whenall in the for loop explained above? That way it will only send x amount at a time? – Zapnologica Jul 17 '14 at 12:37
  • 1
    Parallel.ForEach will block until all tasks are complete, so a Task.Whenall is not required. Within the block, it makes sense to do a synchronous send, I agree. I would try to test and measure any benefits of this approach, as i'm not sure you will gain very much performance-wise. – Patrick Allwood Jul 17 '14 at 12:59
  • The only issue with that approach is, If one of the tasks takes long for some reason, Say it has a network issue, Then my system will not be executing any tasks while it waits for that. But I think this is just going to be one of the cons, – Zapnologica Jul 17 '14 at 13:35
  • @Zapnologica A network issue shouldn't affect how long *starting* the network operation (which is what `BeginSend` does) takes. – svick Jul 17 '14 at 15:11
1

You can try using a TaskCompletionSource or the Task.FromAsync method to convert your SendSignal method to a Task returning one. Then you can just create a list of tasks and await the result after kicking off all the notifications.

The code might look something like this (untested and uncompiled):

public async Task NotifyObservers()
{
    List<Task> notifyTasks = new List<Task>();

    foreach (var observer in registeredObservers)
    {
        if (observer != null)
        {
            notifyTasks.Add(observer.OnMessageRecieveEvent(new ObserverEvent(item)));
        }
    }

    // asynchronously wait for all the tasks to complete
    await Task.WhenAll(notifyTasks);    
}

 public async Task OnMessageRecieveEvent(ObserverEvent e)
 {
    await SendSignal(e.message.payload);
 }

  private Task SendSignal(Byte[] signal)
  {
        if (!state.WorkSocket.Connected)
        {
            CloseConnection();
            return Task.FromResult<object>(null);
        }
        else
        {
            var tcs = new TaskCompletionSource<object>();

            try
            {
                // Sends async
                state.WorkSocket.BeginSend(signal, 0, signal.Length, 0, (ar) =>
                {
                    try
                    {
                        var socket = (Scoket)ar.AsyncState;
                        tcs.SetResult(socket.EndSend(ar));
                    }
                    catch(Exception ex)
                    {
                        tcs.SetException(ex);
                    }

                }
                , state.WorkSocket);
            }
            catch (Exception e)
            {                    
                log.Error("Transmission Failier for ip: " + state.WorkSocket.AddressFamily , e);
                tcs.SetException(e);
            }
        }

       return tcs.Task;
    }
NeddySpaghetti
  • 13,187
  • 5
  • 32
  • 61