[RESOLVED] Multi threading access to module level SEQUENTIAL counter
I've got this code
Code:
Public Class Form1
Dim m_listener As HttpListener = Nothing
Dim m_asyncCount As Integer = 0
Private Sub GetContextCallBack(ByVal result As IAsyncResult)
'Dim listener As HttpListener = CType(result.AsyncState, HttpListener)
m_asyncCount += 1
Dim acHold As Integer = m_asyncCount
DisplayInfo("In Callback with " & m_asyncCount.ToString & "...")
The GetContextCallBack is multi-threaded.
I am incrementing m_asyncCount but when I went to use it later in the GetContextCallback function it was sometimes "incremented" a second time by the call back running again while this one waited to complete (some take longer - they process large memory streams).
At any rate I put the acHold in place to hold the value - and it works...
But I'm leary to do it this way - as I've already seen it can step on it's own toes.
Is there a way to use a DELEGATE to accomplish this in a cleaner fashion?
Ultimately I believe I'm going to get the sequential # from an identity column in a database INSERT - but wanted to test out my proof-in-concept before I got to that point...
*** Read the sticky in the DB forum about how to get your question answered quickly!! ***
Please remember to rate posts! Rate any post you find helpful - even in old threads! Use the link to the left - "Rate this Post".
Re: Multi threading access to module level SEQUENTIAL counter
It can actually increment a second time during the +=1 call, depending on when the OS switches context, which may not even be necessary on a multi-core platform. This could result in a non-increment in some situations:
Thread A reads in the value, context switches to thread B. Thread B reads in the value, increments and restores the new value, context switches back to A, A increments the value it had read in (in a register that was restored when the context switched), increments that value, then restores the new value. Both threads A and B have incremented the value, yet the value is only 1 higher because the increment by B was overwritten by the increment from A. It isn't real likely, but it can happen.
The only safe way to handle that is to use one of the variety of synchronization methods before you perform any action other than simply reading the value.
Re: Multi threading access to module level SEQUENTIAL counter
If it's getting run a second time while it's still completing the first, then you should use the Monitor class on it, and the Monitor(object).TryEnter method:
Code:
Public Class Form1
Private m_listener As HttpListener = Nothing
Private m_asyncCount As Integer = 0
Private monOb As New Object
Private Sub GetContextCallBack(ByVal result As IAsyncResult)
If Threading.Monitor.TryEnter(monOb) Then
Try
'Dim listener As HttpListener = CType(result.AsyncState, HttpListener)
m_asyncCount += 1
Dim acHold As Integer = m_asyncCount
DisplayInfo("In Callback with " & m_asyncCount.ToString & "...")
'... any other code here...
Finally
Threading.Monitor.Exit(monOb)
End Try
End If
End Sub
This way, the first callback grabs the Monitor object and locks down the routine. It only releases it when it's done. If a second callback happens during that time (I'm assuming it's for an identical request), it can't get access to the routine because the Monitor object is being held by the first thread and hasn't been released yet, so it jumps out of the routine and exits.
The Monitor class was made for these kinds of problems. Learn it and love it.
Re: Multi threading access to module level SEQUENTIAL counter
Yes, the only thing you would HAVE to syncronize is any line that does more with the variable than simply read it, which means the increment line. Of course, by the time the DisplayInfo came up, the variable may no longer have the same value. The key is that all code that requires a fixed value has to be using only variables that it has exclusive rights to, either because they are local copies, or because you have controlled access to that portion of code such that the shared variable can't be changed by any other code.
Re: Multi threading access to module level SEQUENTIAL counter
Actually - while reading up on Monitor I believe I found the exact solution to this issue
Code:
Private Sub GetContextCallBack(ByVal result As IAsyncResult)
'Dim listener As HttpListener = CType(result.AsyncState, HttpListener)
'm_asyncCount += 1
'Dim acHold As Integer = m_asyncCount
Dim acHold As Integer = System.Threading.Interlocked.Increment(m_asyncCount)
DisplayInfo("In Callback with " & m_asyncCount.ToString & "...")
Interlocked.Increment is designed to increment a global counter in a thread safe fashion and it returns the value - which I hold "locally" in the callback.
Look at the image below - I dragged 7 different SIZE files into a window below - they got processed by the app on the left - finishing at different times - when each is done it SEND's the assignment # to the app on the bottom right that re-displays with the SEQ# in parenthesis in the display...
See how FILE #4 and # 7 got sent back last? They were the largest.
...
*** Read the sticky in the DB forum about how to get your question answered quickly!! ***
Please remember to rate posts! Rate any post you find helpful - even in old threads! Use the link to the left - "Rate this Post".
Re: Multi threading access to module level SEQUENTIAL counter
"Normal" add operations involve several steps; getting the value of the variable, incrementing it, writing it back to memory. As SH says, in a multithreaded environment, it is possible that the variable gets changed inbetween any of these operations, rendering the result of the add operation invalid.
The interlocked increment operation will modify the variable atomically, which is precisely what you want...if I have understood your problem that is
Code:
Dim value as Integer = System.Threading.Interlocked.Increment(m_asyncCount)