Introduction
In my previous article, I introduced a COM component that allows you to create new threads for background jobs in VB and ASP applications. What you need to do is put your code in a method of a COM object, not necessarily written with VB though, and then use the VBThread.dll to execute the said method from a new worker thread.
When writing code to be executed in multithreaded environment, you need to be careful with shared resources. For example, if multiple threads are allowed to update the same global variable simultaneously, it is likely to cause trouble sooner or later. And we do need to use shared resources in many occasions.
For example, one of our VB web applications loads some data from a remote database and keeps it in memory. When the data is requested, the application will use the data already in memory instead of going to the remote database to fetch it every time. This will reduce performance hit. Users typically do not change the data but they are allowed to do so, in which case the application will update data in both places: the memory and the database.
Of course, we don't want multiple users to update the data in memory simultaneously. What we need is a mechanism to lock the data before updating it and unlock it afterwards.
VBLockManager
This is a COM object contained in VBLock.dll written in VC++. The COM object is free threaded and all methods are thread-safe. You need to create only one instance of this object in your application and use it whenever you want. By the way, you can use it in C/C++ applications as well.
Suppose you have a shared global resource you want to protect in a multithreaded application. All you have to do is:
- Come up with a name string for a resource lock.
- Your code has to call the
SetLock method using the name string as input, before accessing the said resource.
- After you are done with the said resource, call the
ReleaseLock method to unlock it.
Here is a sample VB script file that does this:
...
dim oLockManager
set oLockManager = CreateObject("VBLockManager.1")
...
dim sLockName
sLockName = "MyResource001"
...
oLockManager.SetLock sLockName
...
oLockManager.ReleaseLock sLockName
...
Note that the SetLock and the ReleaseLock methods are different from their counterparts in C/C++ programs. Here are more descriptions of these two methods:
- Both methods take a name string as the only argument.
- The name string can be made up of (case-sensitive) letters and numbers.
- A resource lock is global to the process and is identified by the name string only.
- The
SetLock method will block if the same lock is already set.
- A thread will block itself if it makes two consecutive calls to
SetLock using the same lock name string.
- The
ReleaseLock method can be called from a different thread to unlock a resource.
- The
ReleaseLock method can be called multiple times with no problem.
You need to add an extra call to ReleaseLock in your error-handling code in case the normal code execution is skipped.
As I said before, the VBLockManager object is free threaded and thread-safe, you can use the same instance anywhere in your code. But you can create and use multiple instances if you want to. There is also a TrySetLock method which works the same way as SetLock, except that it won't block. The TrySetLock method returns True if the lock is set successfully, otherwise it returns False.
Locking a Systemwide Resource
What if you have several different applications working together and you want to protect a system-wide resource so that only one process can access it at a time? VBLockManager provides methods SetLockPersist, ReleaseLockPersist and TryLockPersist, just for that purpose. These methods are the same as the ones described above, except that the locks are now global to the machine. Here is another VB script sample file demonstrating the use of these methods:
...
dim oLockManager
set oLockManager = CreateObject("VBLockManager.1")
...
dim sLockName
sLockName = "MyGlobalResource002"
...
oLockManager.SetLockPersist sLockName"
' then access the global resource any way you want
...
' after you are done, call the ReleaseLockPersist method so
' that other threads can access the global resource
wscript.echo "Click OK when done"
oLockManager.ReleaseLockPersist sLockName
...
If you run two instances of the above code, you will see that the second instance will block until you click the OK button in the first instance.
Performance and other issues
I am sure the ideas described in this article is not new. As you may have guessed, I used the Win32 APIs to implement VBLockManager. For details, please look for Windows synchronization functions (such as InitializeCriticalSection, CreateMutex, etc.) in MSDN.
Since it takes a COM method call to lock or unlock a resource, the performance of this tool cannot be compared with code that does similar things in C/C++. Actually, performance was not my main concern when writing the tool. What I want is a tool that allows me to do multithreaded programming easily in VB and ASP applications. For the web applications I have tested, this tool does not seem to make anything noticeably slower. But, it all depends on what you want to do and how you do it. For example, if you use over a thousand locks in your application, the performance will drop quickly. If you use less than a hundred locks, the performance seems to be bearable.
The ReleaseAll method releases all in-process locks, the ReleaseAllPersist method releases all system-wide locks. The system-wide locks are stored in file VBLock.dat located in the same folder as VBLock.dll. This file is automatically created and updated. You can view it using notepad.exe. Deleting this file will be equivalent to releasing all system-wide locks.
And finally, you have to register VBLock.dll on your machine to make things work. :-)
Thank you for reading my articles.
Recent updates
- 10/27/2003
- Added the
GetAllLocks method which returns a string that contains all in-memory locks.
- Fixed a bug in the previous version with in-memory locks.