Quick muliti thread question

helios79

New member
Joined
Oct 23, 2010
Messages
1
Programming Experience
5-10
What I have is a web service that can take up to hundreds of hits per second at times on a quad core dedicated server running IIS7.

This web service does it's thing and as part of it's process has a cache object with a filesystem dependency. When the cache expires (about 15 seconds on average), I use that as a trigger to fire a separate thread off to execute an external routine that I don't want to block the normal request that came in and triggered it in the first place.

What I need is to ensure that this thread only gets called once before it completes. For instance, when my cache object expires, the webservice is taking so many hits that sometimes it can get several hits before the cache object is re-generated, which means the code that creates the thread can get called several times concurrently as well. I only want it to get called once (until it completes) because there is no reason to have concurrent threads executing this routine, ever....

For now I have a Boolean variable that is set to false right before the code that creates my new thread. When the thread completes and hits the callback, that variable is reset to true, letting me know that the thread has completed it's work and is good to be executed again, next cycle around.

I'm not sure this is the best way to accomplish what I'm wanting to do - is there a better way to ensure that only one of these threads is ever run at one given time?
 
Most people's multi-threading needs are fairly modest so most people don't stray from the usual: the BackgroundWorker or Thread class and maybe the ThreadPool class and the odd SyncLock statement. The System.Threading namespace contains various other classes to aid in thread synchronisation though. Two that will help in your case are the Interlocked class and the ReaderWriterLockSlim class.

The Interlocked class provides methods that allow you to perform simple operations and guarantee that they will be atomic. In your case, you will have a flag that indicates whether the cache is being refreshed. Using the Interlocked class, you can test the value of that flag and set it on one thread as an atomic operation, ensuring that another thread doesn't slip in between the test and the set and start refreshing the cache too. I'm not sure how you're expiring your cache but you might use something similar for that.

The ReaderWriterLockSlim class allows you to synchronise access to a shared resource such that multiple threads can read the resource simultaneously but only one thread can ever write to the resource at a time, and reading and writing can never occur simultaneously. In your case, the cache is the shared resource. You can have as many threads as you like reading data from the cache but, as soon as a thread gets a write lock to refresh the cache, no other threads can read until the write lock is released.

Your code might look something like this:
VB.NET:
'Zero for False and non-zero for True.
Private cacheNeedsRefreshing As Long

'Zero for False and non-zero for True.
Private cacheBeingRefreshed As Long

Private cache As SomeClass

Private lock As New ReaderWriterLockSlim

Private Function GetData() As String
    'The first expression gets the value of cacheNeedsRefreshing as a Boolean in an atomic operation.  It evaluates to True if the cache needs refreshing.
    'The second expression tests whether cacheBeingRefreshed as a Boolean is False and, if so, sets it to True.  It evaluates to True if the cache is not currently being refreshed.
    If CBool(Interlocked.Read(Me.cacheNeedsRefreshing)) AndAlso
       Not CBool(Interlocked.CompareExchange(Me.cacheBeingRefreshed,
                                             CLng(True),
                                             CLng(False))) Then
        'Start a new thread to refresh the cache.
        'We are guaranteed that this will only be done by one thread at a time.
        Call New Thread(AddressOf RefreshCache).Start()
    End If

    Dim data As String = Nothing

    'Get non-exclusive read access to the cache.
    lock.EnterReadLock()

    Try
        'Get the data from the cache.
        data = Me.cache.Data
    Catch ex As Exception
        Debug.WriteLine(ex.ToString())
    Finally
        'Release the lock on the cache.
        lock.ExitReadLock()
    End Try

    Return data
End Function

Private Sub RefreshCache()
    'Get exclusive write access to the cache.
    lock.EnterWriteLock()

    Try
        'Refresh the cache.
        cache.Data = GetNewData()
    Catch ex As Exception
        Debug.WriteLine(ex.ToString())
    Finally
        'Release the lock on the cache.
        lock.ExitWriteLock()

        'Reset the flag that indicates the cache needs to be refreshed.
        Interlocked.Exchange(Me.cacheNeedsRefreshing, CLng(False))

        'Reset the flag that indicates the cache is being refreshed.
        Interlocked.Exchange(Me.cacheBeingRefreshed, CLng(False))
    End Try
End Sub
 
Back
Top