tags:

views:

171

answers:

3

Hello,

I need to write an app that that will iterate over our database, and perform various anaylsis on each record. In order to do this (partially for the learning exercise in creating plugin support) I want to use a Plugin model.

Currently, I have a simple Interface in my main app, which plugins can Implement. My app then loads all DLL's in a folder looking for ones implementing the Interface.

As you can see in the pseudo-code below, I have to keep performing a loop through all loaded plugins calling the process methods.

Sub ProcessData()
    For Each Record In MyDataSet

        For Each Plugin In MyPluginCollection
            Plugin.ProcessRecord(Record)
        Next

    Next
End Sub

If I wanted each of the plugins to fire asynchronously, how would I track when all the plugins are done?

Sub ProcessData()
    For Each Record In MyDataSet

        # Start all the plugins off on their processing task            
        For Each Plugin In MyPluginCollection
            CreateNewThreadFor Plugin.StartProcess(Record)
        Next

        # Do not start the next record until this record is completed by all plugins
        Dim Complete As Boolean = False
        While Not Complete
            For Each Plugin In MyPluginCollection
                If Plugin.IsBusy Then
                    Complete = False
                End If
            Next
            DoEvents
        End While

    Next
End Sub

Or am I just opening myself up for a world of pain trying to multithread it in this way?

A: 

You want to read up on ManualResetEvents. They will do the trick. http://www.codeproject.com/KB/threads/AutoManualResetEvents.aspx

Thats not to say you aren't entering a world of pain, however.

here is an example. paste into a command line app

var r = new Random();
Action a = () => Thread.Sleep(r.Next(5000));

Action<int> process = i => //represents your plugin
{
    Console.WriteLine("Thread " + i + " started");
    Thread.Sleep(r.Next(5000));
    Console.WriteLine("Thread " + i + " finished");
};

var mres = new List<ManualResetEvent>();
for (var i = 0; i < 10; i++)
{
    var count = i;
    var mre = new ManualResetEvent(false);
    new Thread(() =>
                   {
                       process(count);
                       mre.Set();
                   }).Start();
    mres.Add(mre);
}
ManualResetEvent.WaitAll(mres.ToArray()); //try commenting this line
Console.WriteLine("All threads finished. Press enter to continue");
Console.ReadLine();
mcintyre321
I'm not so familiar with the C# syntax, but in your example you create a new thread consisting of the process call, and also the mre.set. I've only ever used the AddressOf way of starting threads.How do I get the mre.set to fire?
Cylindric
Am I correct in assuming that the mre.Set() has to happen on the same thread as the "work", so that when the work is done the MRE clears?
Cylindric
Yes, if you do it on the main thread, it'll be 'Set' too early.
mcintyre321
http://msdn.microsoft.com/en-us/library/system.threading.manualresetevent.aspx should help with the syntax
mcintyre321
It was the new Thread(()=>{...}) bit I have trouble with, not the ManualResetEvent itself.
Cylindric
well you can try the method used here to pass a variable to a thread in VB - pass in the MRE http://www.developmentnow.com/g/38_2005_6_0_0_545497/ThreadPool-QueueUserWorkItem.htm
mcintyre321
A: 

Best way would be to extend your Interface with an Event "ProcessCompleted" and checking within that event if every PlugIn is ready (f.e. with a Property "ProcessInProgress").

Bobby

Bobby
A: 

An alternative to using manual reset events could be:

ForEach plugin
   Create new thread for plugin
   Add thread to thread list
   Start the plugin method execution
Next

ForEach Thread in thread list
   Thread.Join() //blocking call until Thread is completed

With the anonymous and extension methods this can achieved with very few lines. Here's an example in C# (not tested):

List<Thread> lstThreads = new List<Thread>();
Thread objThread;

foreach(Record r in MyDataSet)
{
    lstThreads.Clear();
    MyPluginCollection.ForEach(delegate(Plugin p)
    {
        objThread = new Thread(new ThreadStart(delegate() 
        {
            p.StartProcess(Record);
        }));
        lstThreads.Add(objThread);
    });
    lstThreads.ForEach(t => t.Start());
    lstThreads.ForEach(t => t.Join());
}
Peter
Hmm, that seems quite neat. I'll try this one first I think, before setting up the Events method.
Cylindric