0

Lets assume that I have a several layers:

  1. Manager that reads data from a socket
  2. Manager that subscribes to #1 and takes care about persisting the data
  3. Manager that subscribes to #2 and takes care about deserialization of the data and propagating it to typed managers that are insterested in certain event types
  4. WPF Controllers that display the data (are subscribed to #3)

As of right now I use

TaskFactory.StartNew(()=>subscriber.Publish(data)); 

on each layer. The reason for this is that I don't want to rely on the fact that every manager will do his work quickly and that ex. Socket manager is not stuck.

Is this a good approach?

Edit Let's say that Socket manager receives a price update There are 10 managers subscribed to Socket manager so when Socket manager propagates the message .StartNew is called 10 times.

Managers #2,#3 do nothing else but to propagate the message by .StartNew to a single subscriber

So ultimately per 1 message from socket 30x .StartNew() is called.

Vojtech B
  • 2,837
  • 7
  • 31
  • 59
  • There is certainly nothing inherently wrong with multi-threading. Can you elaborate more on your specific question? How many `.StartNew( )` lines do you have in total that are running at the same time? – Special Sauce Jun 20 '15 at 21:16
  • Why not using, Task.Run() ? – Transcendent Jun 20 '15 at 21:17
  • @Transcendent Why use it? http://stackoverflow.com/questions/22087005/task-factory-startnew-vs-new-task This link suggests otherwise – Vojtech B Jun 20 '15 at 21:22
  • @SpecialSauce Please see updated question. – Vojtech B Jun 20 '15 at 21:22
  • @VojtechB: The `Task.Run` is a newer way of running Tasks. At the time we did `TaskFactory.StartNew`, there was no `Task.Run`, but now it's there! Besides if you read the link carefully you see that the accepted answer confirms my claim. – Transcendent Jun 20 '15 at 21:24
  • @SpecialSauce oh, ok. Good to know. If I use Task.Run instead of TaskFactory but in the same way as described in the question - do you see any problem? Also is there a way how to make sure that the tasks are run at the same order? – Vojtech B Jun 20 '15 at 21:27
  • @VojtechBL Is this a distributed or Client-Server app? (because you mentioned sockets), If yes, you can use WCF and set Muitple Concurrency with Sessions, then you don't have to be worried about the concurrency. Anyway I don't really understand the question (what you are doing in specific) – Transcendent Jun 20 '15 at 21:32
  • @VojtechBL The 30x `.StartNew( )` per 1 socket message is concerning to me. How often do socket messages arrive (ie, how many times per second)? Please see my updated answer below. I really think you should consider refactoring your code to use dedicated threads. – Special Sauce Jun 20 '15 at 21:47

2 Answers2

1

It seems a reasonable approach.

However, if one could meaningfully do:

subscriber.PublishAsync(data).LogExceptions(Log);

Where LogExceptions is something like:

// I'm thinking of Log4Net here, but of course something else could be used.
public static Task LogExceptions(this Task task, ILog log)
{
  return task.ContinueWith(ta => LogFailedTask(ta, log), TaskContinuationOptions.OnlyOnFaulted);
}
private static void LogFailedTask(Task ta, ILog log)
{
  var aggEx = ta.Exception;
  if(aggEx != null)
  {
    log.Error("Error in asynchronous event");
    int errCount = 0;
    foreach(var ex in aggEx.InnerExceptions)
      log.Error("Asynchronous error " + ++errCount, ex);
  }
}

So that fire-and-forget use of tasks still have errors logged, and PublishAsync in turn makes use of tasks where appropriate, then I'd be happier still. In particular, if the "publishing" has anything that would block a thread that can be handled with async like writing to or reading from a database or file system then the thread use could scale better.

Jon Hanna
  • 110,372
  • 10
  • 146
  • 251
0

Regarding Task.Run vs. TaskFactory.StartNew, they are essentially identical under the hood. Please read the following link: http://blogs.msdn.com/b/pfxteam/archive/2014/12/12/10229468.aspx

Even though these methods use the ThreadPool for decent performance, there is overhead associated with constantly creating new Tasks on-the-fly. Task is generally used more for infrequent, fire-and-forget type workload. Your statement of "30x .StartNew() per 1 message from the socket" is a bit concerning. How often do socket messages arrive? If you are really concerned with latency, I think the better way of doing this is that each manager should have its own dedicated thread. You can use a BlockingQueue implementation so that the threads are waiting to consume a parent input item in the parent's queue. This would be preferable to a simple spinlock, for example.

This is the sort of architecture used regularly in financial market messaging subscription and decoding that needs the fastest possible performance. Also keep in mind that more threads do not always equate to faster performance. If the threads have any shared data dependencies, they will all be contending for the same locks, causing context switching on one another, etc. This is why a preset number of dedicated threads can usually win out vs. a greater number of threads created on-the-fly. The only exception I can think of would be "embarrassingly parallel" tasks where there are no shared data dependencies at all. Note that dependencies can exist on both the input side and the output side (anywhere there is a lock the threads could run into).

Special Sauce
  • 5,338
  • 2
  • 27
  • 31
  • It's hard to believe that creating 30 objects will be a problem in a WPF app these days. Its even harder to beleive you'll implement a better algorithm to manage your own treads than the thread pool manager would. – Ventsyslav Raikov Jun 20 '15 at 23:01
  • 1
    It all depends on the latency requirements of the app. The OP has suggested the socket listener might receive a *price update* message that suggests to me he may be writing an application that listens to exchange (financial markets) messages. Such streams can dissemenate 1000s of msgs/sec. In this case, low latency can be a tremendously important business requirement. As I've written about a dozen low-latency exchange connectors, I can say with certainty that dedicated threads is the way to go in this scenario. I would have been fired had I developed a solution with a bunch of cascading Tasks. – Special Sauce Jun 21 '15 at 00:36