Apartment Threading: Those little details that make a world of difference between VB6 and .NET
by Mauricio Rojas, on Jan 31, 2020 6:26:36 AM
Ok. Let go back a few years into early 90s, those great years just after the Rick Astley hits (“Never gonna give you up…”).
At that time, if you where a hip programmer you probably were writing code using VB5 or VB6.
Let’s say that maybe you were assigned to do some code using multithreading. Then probably you should remember a little about
Apartment Threaded and ActiveX DLLs
Back to the 90s
Apartment-Threaded ActiveX DLLs
On the VB6 world, one easy and reliable way to create multithreaded servers was use an in-process server.
An in-process server doesn’t require any code for creating or managing threads or apartments.
This means that the application code for managing threads and apartments is handled by somebody else (in this case that someone is COM)
With an ActiveX DLL project your objects take advantage of the threading used by the hosting application. All you have to do is make sure that your DLL project is marked for apartment threading.
Apartments and Global Data
In VB6 public variables defined in standard (.bas) modules aren’t really “global variables”.
These public variables are scoped at the apartment level.
Objects in separate apartments see different instances of these public variables.
It seams that when VB6 added support for apartment threading, they had to decide how to handle the data defined in standard modules.
If they decided to store this data in global memory, someone would have to be responsible for synchronizing access to it. Instead, it was decided to store the data defined in a standard module in thread-local storage (TLS).
When the VB6 runtime creates a new apartment, it also creates a fresh copy of every variable defined in a standard module in TLS.
The thread in each new apartment also calls Sub Main, which lets you initialize the data in TLS just after it creates the new STA.
The reasoning behind this decision is sound. Visual Basic programmers don’t have to deal with the burden of synchronization. It’s always taken care of behind the scenes.
Back to the Present
Well enough history. Lets take the DeLorean an come back to the present.
VB6 is not longer king. And you are now working in .NET.
After a successful migration you are now dealing with a migrated component for an application that was written in ASP and ActiveX Dll and you start nothing that it ocassionally crashes !!@#@!
The new kids are perplexed. What is happenning!! Why is it not working?
For a senior developer like me this is just a quick glance in to the past. You feel like reading bedtime stories to your kids:
… One upon a time… There was VB6 and you had an ActiveX DLL project that contained a BAS module with a variable declaration like:
' ...(in the SomeData.bas file) Public LoginName As String
… And this variable was magically kept into its out appartment automatically … and every lived happily ever after.
In summary for that scenario in VB6, each thread works with a different set of global variables.
In .NET threads can access all variables at the same time. Therefore the .NET component exposes the same LoginName field to all threads and that means that other thread can write to that field and maybe they can write over values stord by some other threads.
C# and the .NET platform in general supports the supports the
This attribute can be applied to static class fields
[ThreadStatic] public static string LoginName;
If a field is marked with
ThreadStatic it will be stored in the Thread Local Storage area, and this separation is automatic.
Just remember, in migration scenarios like the one I just described you can use
ThreadStatic to provide an equivalent behaviour in C#.
In ASP.NET each request is processed by a different thread, so if you are instantiating your components during a request proccesing and destroying it at the end, this is pretty much the same.