Parallel.For for .NET 3.x

November 12th, 2009 by Andy No comments »

.NET 4.0 includes an interesting class called Parallel, with a For method that allows the iterations of a for loop to be executed concurrently, like this:

Parallel.For(0, 10, i => Console.WriteLine(i));

I’ve done a lot of multithreading over the years, but I must confess it’s never occurred to me to apply concurrency in this way, at the level of a simple for loop.   I’m not sure how often I’ll use it in practice, but my current work project is .NET 3.5 so I wanted to have a .NET 3.x version of Parallel.For to play with.   The code is shown below. 

Some basic tests reveal that at the extremes the performance is not as good as the .NET 4.0 version, but for more common scenarios it seems to be comparable.  

static class Parallel30
{
    public static void For(int fromInclusive, int toExclusive, Action<int> action)
    {
        using (var doneEvent = new ManualResetEvent(false))
        {
            int iterations = (toExclusive - fromInclusive);
            int iterationsCompleted = 0;
            Exception actionEx = null;

            WaitCallback iterationCode =
                (arg) =>
                {
                    try
                    {
                        action((int)arg);
                    }
                    catch (Exception ex)
                    {
                        actionEx = ex;
                    }

                    int completed = Interlocked.Increment(ref iterationsCompleted);
                    if (completed == iterations)
                        doneEvent.Set();
                };

            for (int i = fromInclusive; i < toExclusive; i++)
            {
                ThreadPool.QueueUserWorkItem(iterationCode, i);
            }

            doneEvent.WaitOne();

            if (actionEx != null)
                throw new Exception("Action failed", actionEx);
        }
    }
}

Unity and primitive constructor arguments

November 10th, 2009 by Andy No comments »

I’m using the Unity IoC container for the first time, and overall I like it.  But one under-documented and slightly unintuitive area caused me some trouble.

Suppose I have a ThreadPool class:

class ThreadPool
{
   public ThreadPool(int maxThreads)
   {
      ...
   }
}

I can register this in the container as follows:

container.RegisterType<ThreadPool>(new InjectionConstructor(20));

So every time the container creates an instance of ThreadPool, it will pass 20 for the maxThreads argument.   It’s not the most intuitive API, but it does the job.

However suppose I now extend my ThreadPool class to add logging, i.e.:

class ThreadPool
{
   public ThreadPool(int maxThreads, ILogger logger)
   {
      ...
   }
}

How do I modify my container registration to inject the correct ILogger instance?  Of course I could just create a Logger instance and pass it as an extra argument to InjectorConstructor, but that’s not the behaviour I want.   I want the container to resolve ILogger when the ThreadPool instance is being resolved.   Having looked through the documentation, I was coming to the conclusion that this wasn’t possible, but then I came across this post on pnpguidance.net that shows how it’s done:

container.RegisterType<ThreadPool>(new InjectionConstructor(20,typeof(ILogger)));

I suppose using typeof in this scenario makes some sense, but it’s hardly intuitive.  The API could be improved so that I don’t need to specify anything about ILogger resolution at all.   I should only have to specify explicitly the constructor arguments that cannot be resolved automatically, e.g.:

container.RegisterType<ThreadPool>().WithConstructorArg("maxThreads", 20);

Managed DCOM server

October 24th, 2009 by Andy 6 comments »

For my current project I have some old unmanaged C++ code that needs to talk to a new .NET server, remotely.   The channel needs to be fast and secure.   There are a lot of options, but I’ve narrowed it down to two:

  1. WWSAPI for the C++ client, WCF for the .NET server, using NetTcpBinding.  (See here for details.)
  2. DCOM

Option 1 was the strong favourite until Microsoft decided to play silly buggers with the redistribution rights for WWSAPI, so I’ve been forced to investigate option 2.

COM interop through loading .NET components into an unmanaged process is well documented and generally works fine, but accessing a .NET server remotely via (D)COM is not so well documented, and indeed it’s not even clear if it’s supported by Microsoft.   But it does seem to work, and it’s surprisingly simple once you discover the RegisterTypeForComClients method on the RegistrationServices class.   This is basically a wrapper for COM’s CoRegisterClassObject.

Here’s the C# server code:

[ComVisible(true)]
public interface ICalculator
{
    int Add(int x, int y);
}

[ComVisible(true)]
[ClassInterface(ClassInterfaceType.None)]
public class Calculator : ICalculator
{
    public int Add(int x, int y) { return x + y; }
}

class Program
{
    [MTAThread]
    static void Main(string[] args)
    {
        var regServices = new RegistrationServices();

        int cookie = regServices.RegisterTypeForComClients(
            typeof(Calculator),
            RegistrationClassContext.LocalServer | RegistrationClassContext.RemoteServer,
            RegistrationConnectionType.MultipleUse);

        Console.WriteLine("Ready"); Console.ReadKey();

        regServices.UnregisterTypeForComClients(cookie);
    }
}

and the C++ client code:

#import "dcomserver.tlb" no_namespace raw_interfaces_only

int _tmain(int argc, _TCHAR* argv[])
{
    CoInitializeEx(0, COINIT_MULTITHREADED);

    {
        CComPtr<ICalculator> spCalc;
        spCalc.CoCreateInstance(__uuidof(Calculator), 0, CLSCTX_LOCAL_SERVER);

        long result = 0;
        spCalc->Add(10, 20, &result);

        cout << result << endl;
    }

    CoUninitialize();
    return 0;
}

(The simplest way to generate the tlb file to #import is to run regasm /tlb DcomServer.exe)

There may well be some gotchas with this approach – I haven’t tested it thoroughly yet – but it seems a promising option if the WWSAPI licensing issues can’t be sorted out.