Thursday, April 27, 2006

Stacking "using" blocks together

I had always been thinking of using the using-block to instantiate multiple disposable objects so I don't have to remember explicitly calling Close() or Dispose() on them:

            using (IDisposable one = new DisposableOne(), 
                   IDisposable two = new DisposableTwo())
            {
                // Compile time error
            }


But unfortunately C# does not support that. So as a work around, one would do these nested using statements to do the trick:

            using (IDisposable one = new DisposableOne())
            {
                using (IDisposable two = new DisposableTwo())
                {
                    // Ops. Code is now indented
                }
            }


But now, you have messed up your pretty-looking indentation of your code. Remember back in the acadamia your programming class instructor told you that all your if-else/for/while statements will work without curly brackets? Well, surprise, same case for using blocks:

            using (IDisposable one = new DisposableOne())
            using (IDisposable two = new DisposableTwo())
            {
                // This works!                
            }


Time to show off to your pairing friend you can write prettier code than him/her =D

Friday, April 21, 2006

Multi-threading applications tips

Recently I have been involved with a multi-threading application, and throughout development I have been acquiring tips and gotchas here and there. One of the fun things about such applications is that you get to play with some sort of almighty powerful server box. In my case, a cool 8-processor dual core box with 8 GB RAM... How much is it? $70,000. =)

Test your application in a comparable multi-processor testing server. Better yet, test it on your production box
Yes, you heard me. Usually multi-threading applications are performance critical. Otherwise how else can you justify a pricy machine and more complicated code? If you are not able to perform testing on the actual production box, chances are the users will ultimately find the problems. Which means developers, QA, and project managers all have to work OT hours to fix the problem and push those fixes to production, plus the number of phone calls and help desk tickets you have to make. When you multiply these hours and stress, the justification of having another pricy testing server before going into production all of a sudden is not too pricy anymore.

Server GC vs. Workstation GC
.NET garbage collection behaves differently when it is installed on a single processor machine versus on a multi-processor machine. If you install it on a single processor box, it is in Workstation GC mode, meaning there is at most one thread doing garbage collection. When you install .NET onto a multi-processor box, it is installed as Server GC mode, meaning there is one GC thread per CPU. I strongly advise that the environment you deploy to before your production environment to be a multi-processor machine and have Server GC mode turned on. That way you are testing more closely to the real environment. In performance-intensive applications, garbage collection, albeit automatic, is usually worth monitoring as well.

Raise an event thread-safe
In C#, many of us have raised an event this way. In fact, this is the way most books/lectures/tutorials demonstrate it.

    1 public class Battery
    2 {
    3         public const int LOW_BATTERY_LEVEL = 20;
    4 
    5         public event EventHandler LowBattery;
    6         public event EventHandler Depleted;
    7 
    8         private int remainingBattery = 100;
    9 
   10         private void OnLowBattery()
   11         {
   12                 if (LowBattery != null)
   13                 {
   14                         LowBattery(this, EventArgs.Empty);
   15                 }
   16         }
   17 
   18         private void OnDepleted()
   19         {
   20                 if (Depleted != null)
   21                 {
   22                         Depleted(this, EventArgs.Empty);
   23                 }
   24         }
   25 }


Did you know that this is not thread-safe? Thread A could be executing this code, does a null check at line 12, when it is just about to raise the event, Thread B grabs the subscriber and unregisters/unwires the event. When Thread A resumes execution, it tries to raise the event when no one is subscribing to it. Null reference exception.

In order to make the code thread-safe, we can take advantage of delegates are value-types, in other words, you can only create"copies" of them, but not by reference.

   10 private void OnLowBattery()
   11 {
   12         EventHandler handler = LowBattery;
   13         if (handler != null)
   14         {
   15                 handler(this, EventArgs.Empty);
   16         }
   17 }


Use of lock(this) and lock(typeof(Foo))
Every C# programmer is aware of the lock keyword. It prevents multiple threads from referencing the resource while the resource is being modified. When you want to modify a member variable thread-safe, you can go and lock the object that contains it:

public Foo()
{
            lock(this)
            {
                    _memberVariable = "SOMETHING";
            }
}


But what about static variables? Well, interestingly the lock statement allows you to lock a type object as well:

public class Foo
{
            public static string StaticVariable = null;
 
            public void SetUpStatic()
            {
                    lock (typeof(Foo))
                    {
                            StaticVariable = "SOMETHING";
                    }
            }
}


Give you threads names
Yes. Threads in the old days have only a thread id, which gets generated every time when a new thread is genereated, and that makes debugging very painful. Now in .NET you can programmatically give each thread your application creates a name with its t.Name property. Use them, then in your VS.NET Debug/Windows/Threads (Ctrl-Alt-H while debugging) you will see each thread with their name. Now they look much more friendly. Better yet, name your threads after each project manager you have worked with =)

Use .NET synchronization classes
Don't tell people you know how to write multi-threading applications knowing only how to use lock(). If you don't know how Monitor.Wait() and Monitor.Pulse() and the shopping bag of the synchronization classes that .NET provides, you are missing out alot. As a starter, try this excellent article here. It's a must read for anyone programs C# and multi-threading.

Wednesday, April 19, 2006

Checking duplicate Sql statements in Excel

In my current project we are doing database Continuous Integration (CI). This means that every developer will have their own database instance for development, and at every check-in the build server kicks off the CI cycle, which starts getting the latest source code, compile, rebuild the database from scratch through running SQL scripts, re-insert data (reference data) these tables should have, compile, test, etc. When done right, this allows the much-required freedom for developers to develop against the database without interfering other developers' databases, and significantly reduces the time it takes to ask the DBA's to modify the database table for you.

When you acquire from the customer/business analysts a new set of reference data that they would like you to insert into these database tables, I have seen in many cases where these new data conflicts with the data that already exists due to duplication (mostly human error). Since most of these reference data are scripted in text files and there are usually hundreds if not thousands of them, sometimes finding that one duplicating line is like trying to find a needle in a sea.



Fortunately in Excel there is a function to rescue: =COUNTIF(set, to_find).

Usage: Increment count if "to_find" exists in "set".

So to quickly find out which is the offending PK constraint INSERT line, you do this:
=COUNTIF(E3:E11, E3)



And as you see you will quickly find out where that awful INSERT statement is... Time to bug the customer =)