Locking a member Variable in Memory


Variable life time is normally not a problem in VB but but I'm working in the context of a webservice and variable persistence is quite different.

In a normal VB program Member variables are the variables common to all routines.... and not declared in a function or subroutine.

They are assumed to have a lifetime for the lifetime of the program.

In a normal program I can do this:

Class

   Member variable

   Subroutine A

   Subroutine B

end class


The member variable has a lifetime of the program. Static is not a legal option for a member variable.

The environment in a webservice appears to be quite different.

The architecture of my webservice is similar to the above with two webmethods.

Class

   Member variable  (dictionary database)

   Webmethod A  (Asynch database loader)

   Webmethod B (database processing - driven by Client-side Java)

end class

I load the database in A and return in B to find the database empty as it is not being persisted across calls.

Question: How do I lock the dictionary in memory so that it is persisted







Answer this question

Locking a member Variable in Memory

  • MartijnB


    1. If you are not sure why the class is persisting the database between calls, then you need to read up on shared members within classes.

    Marking a member 'shared' causes the member to be shared among all instances of your class. So in your situation, HouseDb is shared, therefore there will only be one instance of it no matter how many times an instance of your WebService gets created (which occurs everytime a method is called on it across the web).

    If shared was removed from the declaration of HouseDb, then LoadDb would load the database everytime an instance of the WebService was created (which as I stated before, occurs everytime a method is called on it across the web). Once a method call has finished, the WebService instance goes out of scope, therefore so does the HouseDb member.

    You need to be aware of what shared is doing. Basically it means that every single method call on the WebService and every single user that calls the WebService will be seeing the exact same instance of HouseDb.

    2. You need to make LoadDb thread-safe. Currently because HouseDb is marked shared, it can mean that multiple threads can access it at the same time.

    This can mean that it possible for ReadDatabase() to be called multiple times. Read up on the SyncLock() construct.

    3. Your ReadDatabase method in the DB class is leaking resources. FileStream is a wrapper around an unmanaged resource and needs to be closed. Setting local variables to Nothing in .NET does absolutely nothing. You instead need to call FileStream.Close().

    VB 2005 has a new construct called Using that makes it easy to do this properly:



    Using fsi New System.IO.FileStream("H:\VirtualRealty\App_data\DwellingFeatures.dbs", FileMode.Open, FileAccess.Read)

    End Using

     



  • Nuno

    Usually I use private shared variables and expose them as shared properties.  You can use synclock to prevent mutilple people from changing the variable at the same time.

  • xguy

    Thanks ken.

    Yeah I know and there's nothing I hate worse than shared variables. I avoid them like the plague.

    They are really restrictive in their use, No Because of thread safety

  • giorgiosf

    I'm not sure you understand what I meant.

    I meant that a shared property in Visual Basic (which is called a static property in C#) is the way to go. Not that C# is the way to go.

  • Michele Leroux Bustamante

    Thank you, David.

    Goodness this is going to be embarassing because my partner has been beating me over the head about C#. I originally come from Digital. All of our languages were case insensitive and had no brackets. I will never really like C or feel comfortable with it which is probably why I settled into basic.

    All of the data is the same. It will never change.

    Thank you all.

  • Chris Themis


    1.) I am sure and have been sure why the data is persisting. I do understand the instantiation and the classes. That is not a problem at all. I fully understand the theory behind this.

    What I do not understand, is why it was this was so simple when nomally using a shared variable such as cursor.position is like pulling teeth to use or expose.

    2.)

    I had already put locking in as a synch technique. Before I read this.

    Dim BinFormatter As New BinaryFormatter()

    Dim obj As Object

    Dim HouseDBLock As New Object



    SyncLock
    HouseDBLock

    Try

    obj = BinFormatter.Deserialize(fsi)

    HouseDb = (CType(obj, Dictionary(Of Integer, List(Of String))))

    Catch e As SystemException

    ReadDatabase = False

    End Try

    End SyncLock

    BinFormatter = Nothing

    fsi.close    ' Thank you.

    obj = Nothing

    HouseDBLock = Nothing

    End Function


    What I used to do in VMS was disk cluster diskshadowing. mainframe synchronization during cluster transistions.

    3.) Thank you for that. Using didn't lend itself well to this. Perhaps after the code is stabilized.....



  • Denispiow

    Well David....

    It may be less than relavant because this works....

    I don't think it should work but it works and I don't know why.

    We have persistence and asynchronicity although we shouldn't in theory. All I did was to declare the dictionary Public Shared and it's letting me do anything I wanted to with it.
     

    Public Shared HouseDb As Dictionary(Of Integer, List(Of String))

    <WebMethod(enablesession:=True, cacheduration:=300)> _

    Public Sub LoadDB() ' Database Preload

    If HouseDb Is Nothing Then

    Dim database As New DB

    database.ReadDatabase(HouseDb)

    End If

    End Sub

     

    <WebMethod()> _

    Public Function RetsDBQuery(ByVal HomeType As String, ByVal Locale As String, ByVal LowValue As String, _

    ByVal HighValue As String, ByVal LowBedrooms As String, ByVal HighBedrooms As String) As String

    If HouseDb Is Nothing Then

        Dim database As New DB

        database.ReadDatabase(HouseDb)

    End If

    If HouseDb.Count <> 0 Then

        Dim sHomeType As String = Server.HtmlEncode(HomeType)

        Dim sLocale As String = Server.HtmlEncode(Locale)

        Dim sLowValue As String = Server.HtmlEncode(LowValue)

        Dim sHighValue As String = Server.HtmlEncode(HighValue)

        Dim sLowBedrooms As String = Server.HtmlEncode(LowBedrooms)

        Dim sHighBedrooms As String = Server.HtmlEncode(HighBedrooms)

        Return Inquiry(sLowValue, sHighValue, sHomeType, sLowBedrooms, sHighBedrooms, sLocale)

    Else

        Return "Database failed to load" & CurDir()

    End If

    End Function



     

    <Serializable()> Public Class DB ' DBLOADER

    Public Sub New() 'ByRef Dic As Dictionary(Of Integer, ArrayList))

    End Sub

    Public Sub ReadDatabase(ByRef HouseDb As Dictionary(Of Integer, List(Of String)))

    Dim fsi As System.IO.FileStream = Nothing

    If CurDir() = "I:\Visual Studio 8\Common7\IDE" Then ' Debug mode

    fsi = New System.IO.FileStream("H:\VirtualRealty\App_data\DwellingFeatures.dbs", IO.FileMode.Open, FileAccess.Read) ' Open the dictionary in the defaulting directory

    Else

    fsi = New System.IO.FileStream("\App_data\DwellingFeatures.dbs", IO.FileMode.Open) ' Open the dictionary in the defaulting directory

    End If

    Dim BinFormatter As New BinaryFormatter()

    Dim obj As Object

    Try

    obj = BinFormatter.Deserialize(fsi)

    HouseDb = (CType(obj, Dictionary(Of Integer, List(Of String))))

    Catch e As SystemException

    Beep()

    End Try

    BinFormatter = Nothing

    obj = Nothing

    fsi = Nothing

    End Sub

    End Class


    It works exactly as I need and want it to. That's great but I'm very puzzled. I've also put break points in the RetsDBQuery and the break point never goes off where the database is nothing. And there is data in the database.

    It really shouldn't be behaving this way... meaning working. Btw there is a lot more code in the service. This is the I/O engine so to speak and that what we are addressing.



  • M.A.D

    You'll probably stay confused until you actually hear what I'm saying.

    In the past, every time I've tried to use a shared variable I get those green squiggles that says "this expression will not be evaluated."

    What a shared variable does is NO mystery at all.

    On the synclock. You mean it's a lock that is only Process or instantiation wide

    That was an error on my part. I'm used to system wide mutexes etc, not something with a granularity that narrow.



  • wkb

    A shared variable will hold the same value in all instances of the class.


  • wat2

    1. I am confused as to why using a shared member is so hard. If anything it is easier because you don't need an instance of the object to access.

    2. The locking you have implemented won't actually work because:

       1. You are locking a new instance of an object each time. So if another thread enters this method, then both threads are locking a different object.
       2. You have placed the lock around the wrong bit of code

    Here is some code showing what it should look like (I haven't compiled this):



    Private Shared LockObject As New Object
    Public Shared HouseDb As Dictionary(Of Integer, List(Of String))

    Public Shared Sub LoadDB()

     If HouseDb Is Nothing Then
      
      SyncLock(LockObject)

        
       If HouseDb Is Nothing Then

        HouseDb = ReadDatabase()
       
       End If

      End SyncLock

     End If

    End Sub

     

    Public Function ReadDatabase() As Dictionary(Of Integer, List(Of String))


     // Read database and return it

    End Function


     

    As I said before, you also don't need to set your local variables to Nothing. It does absolutely nothing.

    3. Using also has the benefit of ensuring that your filestream is closed even in the case of an exception.



  • Scott S Sikora

    Ken I understand that. This dictionary is referenced constantly all through the code.  I also understand the synchronization techniques to which you refer. Which would be consume lots of cpu time.

    I'm used to writing mainframe drivers in kernel mode at IPL8 in assembler. I promise I know how to write thread safe code.

    I will not use shared variables because of their lack of flexibility.

    I cringe everytime I use cursor.position. Almost invarairably I have to write a function that looks like this:

    Private Shared Function Curpos () As Point

       return cursor.position

    End function.

    There are times that I've called the API because I find cursor.position so inconvenient.

    I love vs2005. It's a fine project but if there is one unusable feature it's shared variables and the thread safe restrictions.

    I think I've only declared one in my life and would far prefer do this synchronously rather than to declare a shared variable. I don't think there is any more irritating in the entire VB world.

    I'm sorry to complain, but shared variables just aren't something that I'll use. I really wish there was a mode if vs2005 that would turn thread safety off.

    This dictionary is the heart and soul of this and it is referenced constantly. The irony here is that this dictionary is read-only, no-one ever writes to it after it is de-serialized.  It will have 866 records with 566 variables each and the data will remain unchanged forever.


  • Jan Byvaly

    The data is being persisted because a new instance of the WebService class is being created on the server for each call.
     
    If the data is common to all users, a shared (static in C#) property is the best approach. If the data is scoped per user, then persistance via session state is probably the best way to go.

  • Locking a member Variable in Memory