views:

95

answers:

1

Hi guys,

I need somebody with high skills in threading and event raising.

I have an abstract class A and two concrete classes C1, C2 (e.g plugins).

Since I need them to communicate between each other, like "plugin-application" "plugin-plugin" communication, I have a method ExecuteCommand in the abstract class which should accomplish this. This function raises an event to the application in order to process a certain command and return the result (e.g if one plugin needs data from the app it calls ExecuteCommand from the base and waits for the result which comes with the event handler processed on the application).

protected object ExecuteCommand(SvcCmdType cmdType, params object[] inputParams)
{
  // this code has been simplified
  SvcCommandEventArgs eventArgs = new SvcCommandEventArgs(cmdType, inputParams);

  // generate processing command event (it requires to fill in the result)
  OnProcessingAppCommand(this, eventArgs);

  return eventArgs.OutputParamsList; 
}

The problem is:

If each one of C1 and C2 have different threads behind and call simultaneously ExecuteCommand from inside their own threads then for sure my design will be broken and the result returned will be unexpected.

What is the best design for this scenario? I was thinking to use inside ExecuteCommand asynchronous calls like using AsyncOperation... but is it the right way?

edited: I guess I am looking for: is the synchronous or asynchronous way better for my scenario? Or, shall I have the app event handler processed inside a plugin's thread or synchronized somewhere in my main thread?

I would really appreciate some good explanations for your recommendations

Thank you.

+1  A: 

The usual simple method of doing thread synchronization on a common resource or code block is to use a mutex (or, in this case, a critical section). Use the lock statement:

http://msdn.microsoft.com/en-us/library/c5kehkcz(VS.71).aspx

This article says to do locking on the "this" pointer, but that can be dangerous, as outside callers could also acquire the same lock, which could break your program. Do your locking on a private class variable.

Here is some modification of your example code to incorporate locking/a critical section:

class SomeClass : ISomeInterface
{
  protected object ExecuteCommand(SvcCmdType cmdType, params object[] inputParams)
  {
    lock(executeCommandLock)
    {
      SvcCommandEventArgs eventArgs = new SvcCommandEventArgs(cmdType, inputParams);
      OnProcessingAppCommand(this, eventArgs);
      return eventArgs.OutputParamsList; 
    }
  }

  private Object executeCommandLock = new Object();
}

Edit:

(paraphrasing from comments). You mentioned that you might want to process all calls to ExecuteCommand on a single thread, asynchronously. You may be able to accomplish this with the Dispatcher class:

http://msdn.microsoft.com/en-us/library/system.windows.threading.dispatcher.aspx

Get a reference to the dispatcher on one thread. Pass that reference to the other threads. When those threads want to call ExecuteCommand, they use dispatcher.BeginInvoke. Since they use BeginInvoke, all calls to ExecuteCommand will then operate asynchronously, and not block on that thread. However, each version of ExecuteCommand will be queued up, and run sequentially dispatcher thread.

Merlyn Morgan-Graham
Hi Merlyn. Thank you very much for your fast response. So you propose to lock the code inside ExecuteCommand which means will block all other plugins that want to communicate in the same time. Maybe asynchronous calls are better then? Actually I guess this is my main question: synchronous or asynchronous is better for this scenario?
Cristi
@Cristi: Asynchronous calls are threads with wrappers around them. They still execute at the same time. If they share any state (any functions they call modify/read the same variables), you have to synchronize the threads/asynchronous calls. You can't simply test all the bugs away, either, because multi-threading is non-deterministic. Your app will only blow up under just the right (wrong) conditions. Due to murphy's law, this will happen on a client's machine in Antarctica.
Merlyn Morgan-Graham
@Cristi: As for synchronous vs asynchronous, you may still be able to support asynchronous if you somehow manage to give each thread/call it's own set of state. If they don't share anything, they can't break each other.
Merlyn Morgan-Graham
@Cristi: Depending on your code, you may be able to make the critical section much smaller, so you only block when multiple threads are trying to access that single resource.
Merlyn Morgan-Graham
Hi Merlin. Your answers are helpful. I understand the idea you proposed. I am now not sure if this solution is enough for me or if there is a better way. With the "lock" we will have the event handler processed inside internal threads. Do you have any idea how can I execute the event handler from outside, only one synchronized thread, not a C1,..,Cn internal thread?
Cristi
@Cristi: Oh, it sounds like you want ExecuteCommand to be called asynchronously, and not block. I was confused, somehow I thought you meant that you wanted to make C1,...,Cn asynchronous, which they already are :) There may be something built into .NET to do what your are asking (dispatcher?).
Merlyn Morgan-Graham
@Cristi: If not, you could roll your own thread dispatch queue. Make a private queue of Action objects. Make a method that will enqueue an action. Put the body of the enqueue method in a critical section, locking on the queue. When calling the enqueue method, use an anonymous delegate/lambda to wrap your call, so it is not executed until dequeued. Make a dequeue method, that will pop off the queue. Make the body of that dequeue method a critical section, also locking on the queue. Make a single thread that loops, sleeps, pops off the queue, and executes the Action.
Merlyn Morgan-Graham
@Cristi: Please look into the dispatcher first, though. If it works for you, awesome. If it doesn't, I'll give you some sample code that makes my last comment a little more obvious. Probably hard to read, as that giant block of text there :)
Merlyn Morgan-Graham
Merlyn, you are very kind trying to help me here :) Thanks a lot... I should take a look over an example about Dispatcher to understand it... I've never used it before :)
Cristi
@Cristi: I've only had to use it a couple times, within WPF applications. I was making an assumption that they would work in any context. From skimming this page, it looks like my assumption is correct: http://msdn.microsoft.com/en-us/library/system.windows.threading.dispatcher.aspx. Give it a shot, and let me know how it works out :)
Merlyn Morgan-Graham
Dispatcher looks indeed very helpful (is .net 3.0, but i can leave with that). Actually my app is a NT service with some extensions/plugins for data management, state management, etc. If I create this dispatcher in the service main thread and where the processing of the commands will be performed, I should not have problems with synchronization at this level, right?
Cristi
@Cristi: No promises, but that sounds correct to me!
Merlyn Morgan-Graham
I will give it a try now. I accepted your answer because you gave me a good tip how to solve the threading problem. Thank you very much!
Cristi
Merlyn, I would like your help again... if I would not use the Dispacher (although is ok, but in net3.0)... how can I achieve the same with .net 2.0?
Cristi
I did post some information about how you would make one, but Jon Skeet has a (I assume much better) write up here: http://stackoverflow.com/questions/3456129/alternative-of-dispatcher-class-from-net-3-0-to-use-in-net-2-0-context
Merlyn Morgan-Graham
Hi Merlyn :) Glad to see you back. Thank you for your reply. Maybe a dumb question now: how can I marshal code from one thread to another in a windows service? Your idea of the Dispatcher is ok... but it would be an additional thread, not the main one. Dispatcher is good that it can be used to serve from the main thread.
Cristi
@Cristi: I have some ideas about how you would accomplish it, but how is your app structured? What is running on the main thread? Would running items on the main thread block your program in a way that would cause problems?
Merlyn Morgan-Graham