VB.NET - SyncLock vs Mutex

Asked By tom shad on 20-Dec-06 08:21 PM

I am trying to set up a generic write to a text file for my Service which as multiple Threads.  To make this work I need to sync the call.

I tried to use SyncLock but it doesn't seem to work.  Mutex works fine.

Why doesn't the SyncLock?  Which is better to use?

My Mutex code that works is:

*********************************************************
Dim mutexFile As Mutex

Public Sub DebugPoller(ByVal strInfo As String)
  mutexFile = New Mutex(False, "Mutex Name")
  mutexFile.WaitOne()  'Wait until the file is open
  Dim fs As New FileStream("c:\CreditPollerLog.txt", FileMode.Append, FileAccess.Write)
  Dim s As New StreamWriter(fs)
  s.BaseStream.Seek(0, SeekOrigin.End)
  s.WriteLine(Now() & "  " & strInfo)
  s.Close()
  mutexFile.ReleaseMutex()
End Sub
*********************************************************

The code of the SyncLock is almost the same:
**********************************************************
Dim objLock As Object = New Object

Public Sub DebugPoller(ByVal strInfo As String)
  SyncLock objLock
    Dim fs As New FileStream("c:\CreditPollerLog.txt", FileMode.Append, FileAccess.Write)
    Dim s As New StreamWriter(fs)
    s.BaseStream.Seek(0, SeekOrigin.End)
    s.WriteLine(Now() & "  " & strInfo)
    s.Close()
  End SyncLock
End Sub
*****************************************************************

The code seems to be ignoring the SyncLock so the file is missing line of text.  With the Mutext it looks perfect.

Thanks,

Tom

Use Me on SyncLock

Thomas R replied to tom shad on 23-Sep-08 04:44 PM
I think you need to synchronize on the whole object you are locking the code, like this: ********************************************************** Public Sub DebugPoller(ByVal strInfo As String) SyncLock Me Dim fs As New FileStream("c:\CreditPollerLog.txt", FileMode.Append, FileAccess.Write) Dim s As New StreamWriter(fs) s.BaseStream.Seek(0, SeekOrigin.End) s.WriteLine(Now() & " " & strInfo) s.Close() End SyncLock End Sub *****************************************************************

I disagree...

Javin 007 replied to Thomas R on 18-Dec-09 12:16 PM
"I think you need to synchronize on the whole object you are locking the code..."

You really shouldn't have to.  The idea behind using a generic "ObjLock" is twofold:

1.) It's the equivalent of holding the "talking stick" around a campfire.  Because all objects will be using the SAME "ObjLock" object, only one thread can old this "ObjLock" at a time.  Thus, the first thread grabs it, holds onto it while they do their work, and then lets it go.  Only after it's been let go can the next thread grab it, etc.

2.) You are not allowed to make changes to the object that you're locking.  Thus, if you were to use "SyncLock Me" then the Me object couldn't be modified.  Plus, the other objects wanting to access the file would have to "SyncLock YOU" in order to verify that it's their turn to "hold" the lock.

As for why the Mutex works, and SyncLock doesn't, I don't have an answer there.  Your code LOOKS right from what I can see.  Maybe the objLock isn't actually getting created publicly?  Maybe in your debug, you could set a name for the objLock in one thread, and read the name in the other to see if they're using the same objLock?  I'm just grasping at straws here...

...

Javin 007 replied to Javin 007 on 18-Dec-09 12:18 PM
Wow... Just realized I was answering a 3 year old question.
simon replied to Javin 007 on 27-Sep-10 08:02 AM
Doesn't matter you've helped me! It's 2010 now and I still don't think synclock works in VS2008/vb.net
Javin 007 replied to simon on 27-Sep-10 02:38 PM
You know, I do have ONE possibility here:

Dim objLock As Object = New Object

What happens when you instantiate your objects like this is that the system does not actually CREATE the object until the first time it's referenced. This can cause much confusion when you do this:

If objLock = Nothing Then...

Because what happens here is up UNTIL you call that check, objLock IS nothing, but as soon as you DO the check, THEN it actually creates the object. In fact, you could rewrite this as:

objLock = New Object
If objLock = Nothing Then...

So objLock = Nothing will ALWAYS equal "false."

I wonder if it's POSSIBLE that at the point that you call:

SyncLock objLock

That, because we're dealing with Microsoft here, the system completely confuses itself, and the object either isn't created at all, or the object is created AFTER the SyncLock. This would be the same as calling:

SyncLock Nothing
objLock = New Object

In which case the SyncLock wouldn't lock the objLock, so the next lock trying to lock the same object would in fact NOT be locking the same object at all... (I don't know if this makes any sense when I write it down, but it works in my head.)

The simple way to see if this is the case is instead of doing:

Dim objLock As Object = New Object

Instantiate the variable like so:

Dim objLock As Object
objLock = New Object

Here, you're explicitly instantiating the object and there can be no confusion as to when it's ACTUALLY created.

If SyncLock STILL doesn't work after that, I have no clue. But if that does "fix" SyncLock, then we'll know it's just another Microsoft stupidity.
simon replied to Javin 007 on 27-Sep-10 06:28 PM
Javin hi thanks for the info, it's funny you should focus on that because I've done a lot of googling on this subject and I've seen various examples/samples where some say

Dim objLock as Object = New Object()

and others as

Dim objLock as New Object

which *should* technically be the same thing. But where synclock is concerned I'm not so sure.

Interestingly I'm struggling most with get synclock to work in web services as opposed to applications or windows services.

I've actually had more success with Monitors.  I know this sounds a little unspecific, but that's the trouble - no one seems to  be able to tell if exactly that mutex/monitors/synclock is actually working, they just see the symptons go away and assume it is. Meanwhile their code is now 20% less efficient...

All I want to do is write a web service which can write to a log file from any function and from any thread coming in to it without the log file becoming locked. How hard can that be?

Javin 007 replied to simon on 28-Sep-10 10:59 AM
Hrm...  I'm using VB.NET Express 2010, and haven't actually been able to find any problems with the SyncLocking, no matter how I declared the lock object.  I created a very simple demo to test it, and whether the lock object was declared like: 

Dim objLock As New Object
Dim objLock As Object = New Object
Dim objLock As Object
   objLock = New Object

Any of the three, the SyncLock continued to work.  It's possible that it was fixed in 2010, but I suspect not, as I haven't seen anything mentioning it in the release notes.  Maybe there's an issue with how you're scoping your lock object?

Here's how I did it.  First, I create an object to pass parameters to my thread, since I can only pass one object as a parameter (this has changed with 2K8, but then it only worked with functions, and with 2010 you could pass multiple parameters to subs, but with both Microsoft's "solution" is sloppy and convoluted, so I stick with passing all of my parameters as a single thread parameter object.)

Private Class ThreadParams
  Public FileName As String
  Public objLock As Object
  Public Value As String
End Class

I will be passing a filename, a value for the line to put in the file, and notice that the objLock is explicitly NOT declared as "New" here.  The reason will become more obvious later.

Now I declare all of my objects in the main thread, and fire off both threads simultaneously:

Public Sub Main()
  Dim t1 As Thread, t2 As Thread, thrParam1 As ThreadParams, thrParam2 As ThreadParams
 
  thrParam1 = New ThreadParams
  thrParam2 = New ThreadParams
 
  t1 = New Thread(AddressOf WriteText)
  t2 = New Thread(AddressOf WriteText)
 
  thrParam1.objLock = New Object
  thrParam2.objLock = thrParam1.objLock
 
  thrParam1.FileName = Application.StartupPath & "\Test.txt"
  thrParam2.FileName = Application.StartupPath & "\Test.txt"
 
  thrParam1.Value = "This is the FIRST thread running."
  thrParam2.Value = "This is the SECOND thread running."
 
  t1.Start(thrParam1)
  t2.Start(thrParam2)
 
End Sub

Very important point here:  The lock object for the first, and only the first parameter object is created as "new."  Any subsequent threads need to use that SAME lock object, so the second parameter's lock object gets set equal to the first lock object.  Then I set the other values, and fire up the threads.  The threads just look like this:

Private Sub WriteText(ByVal thrParam As ThreadParams)
  Dim sw As StreamWriter, strLine As String, intTick As Integer
 
  SyncLock thrParam.objLock
 
    sw = New StreamWriter(thrParam.FileName, True)
 
    intTick = System.Environment.TickCount + 1000
 
    Do While intTick > System.Environment.TickCount
      strLine = CStr(System.Environment.TickCount) & " - " & thrParam.Value
      sw.WriteLine(strLine)
    Loop
 
    sw.Close()
 
  End SyncLock
 
End Sub

The threads can receive a single object as their parameter, so I pass the ThreadParams object.  Just before opening the file (starting the writing stream) I lock the objLock to make sure that I'm the only one trying to muck with the file.  I then release the object when I'm done.  When you look at the output file, you'll see that even though these threads were started simultaneously, the first thread will write to it for a full 1 second while the second thread waits patiently for its turn.  Once the object is "unlocked" the second thread then writes to the file for a full second.

You can also use multiple lock objects within a single thread.  Suppose you have multiple files that you will need to write to in the same thread.  You can have a separate objLock for each.  You do have to be cautious about thread deadlocks though, but this can be easily avoided if you make sure that a single thread only locks a single object at any one time. 

Torsten replied to tom shad on 03-Mar-11 03:03 AM
Just a thought ... even, if this topic is very old ...

I don't really think, that the initializiation (Dim objLock As Object = New Object) of the object is the problem. Some years ago I had a very confusing problem with the use of a variable from two different threads. After a while of investigation I found a worthfully hint about it.

Take a look to this attributes (in order of importance):
  • System.ThreadStaticAttribute
  • System.ContextStaticAttribute
  • (System.MTAThreadAttribute)
  • (System.STAThreadAttribute)
My problem was, that my (possibly shared) variable behaved like described in the help text of the "ThreadStaticAttribute", but I didn't . My initialization in thread 1 wasn't visible in thread 2 and vice verca. I can remember, that some default value of my project led to this result (possibly the STA/MTA-appartment-modell, sorry - can't remeber that).
After changing this default value the behavior of my threads was how excepted.

Perhabs this is or was a possible reason for the nonworking synclock in your example?
And a mutex would be unaffected by this - so it's thinkable.

Regards,
Ted.
Allan Hawley replied to tom shad on 21-Jan-12 03:51 AM
SyncLock only works on one program at a time. With web services they run under a web server that spons multiple virtual instances of your program to handle the workload, after a certain amount of idle time these processes are destroyed. Basically if one instance SyncLocks a section of code, a second or third instance have no idea that another program has a SyncLock on that section of code.

Mutexes can work across multiple programs at the same time. I suspect mutexes will also fail when there is more than one server handling your web site. These are called web farms, and are very common with hosted web sites. There could be a CreditPollerLog.txt file on two or more different computers, each ending up with different data.

A central sql server or access database is the best way to save data created by your web service.