What should I SyncLock in this code, and where? - vb.net

I have a class that has two method in it, one calls a class which creates and executes a number of threads, the other is an event handler that handles an event raised when those threads complete (and then calls the first method again).
I understand that the method that handles the event runs in the thread that raised the event. So as such, I SyncLock a member variable that says how many threads are running and subtract one from it:
SyncLock Me ' GetType(me)
_availableThreads -= 1
End SyncLock
So I have a few questions:
Main Question: Should I be SyncLock'ing _availableThreads everywhere in the class - i.e in the method that creates the threads (which adds 1 when a thread is created)
Side Questions related to this question:
I'd usually SyncLock the current instance, but I've seen code that SyncLocks the type instead, so what is the difference between sync locking Me (Current Instance) and GetType(Me)?
Would there be a performance difference between the two? and is there anything smaller I'd be able to lock for the above that doesn't affect anything else - perhaps a separate 'padlock' object created for the sole purpose of locking things within a class?
Note: The sole purpose of _availableThreads is to control how many threads can run at any given time and the threads process jobs that can take hours to run.
Code:
Public Class QManager
Private _maxThreadCount, _availableThreads As Integer
Public Sub New(ByVal maxThreadCount As Integer)
Me.MaximumThreadCount = maxThreadCount
End Sub
Public Sub WorkThroughQueue()
//get jobs from queue (priorities change, so call this every time)
Dim jobQ As Queue(Of QdJobInfo) = QueueDAO.GetJobList
//loop job queue while there are jobs and we have threads available
While jobQ.Count > 0 And _availableThreads <= _maxThreadCount
//create threads for each queued job
Dim queuedJob As New QdJob(jobQ.Dequeue)
AddHandler queuedJob.ThreadComplete, AddressOf QueuedJob_ThreadCompleted
_availableThreads += 1 //use a thread up (do we need a sync lock here?)***************************
queuedJob.Process() //go process the job
End While
//when we get here, don't do anything else - when a job completes it will call this method again
End Sub
Private Sub QueuedJob_ThreadCompleted(ByVal sender As QdJobInfo, ByVal args As EventArgs)
SyncLock Me //GetType(me)
_availableThreads -= 1
End SyncLock
//regardless of how the job ended, we want to carry on going through the rest of the jobs
WorkThroughQueue()
End Sub
#Region "Properties"
Public Property MaximumThreadCount() As Integer
Get
Return _maxThreadCount
End Get
Set(ByVal value As Integer)
If value > Environment.ProcessorCount * 2 Then
_maxThreadCount = value
Else
value = Environment.ProcessorCount
End If
LogFacade.LogInfo(_logger, "Maximum Thread Count set to " & _maxThreadCount)
End Set
End Property
#End Region
End Class

You shouldn't SyncLock the instance or the type. You always want to SyncLock on a variable that is fully within the control of the class, and neither of those are. You should declare a private New Object and use that for your SyncLock.
Private lockObject as New Object()
...
SyncLock lockObject
...
End SyncLock

Unfortunately, you need to do a few things differently here.
First off, I'd recommend avoiding SyncLock, and using Interlocked.Increment and Interlocked.Decrement to handle changing _availableThreads. This will provide thread safety for that variable without a lock.
That being said, you still will need a SyncLock around every access to your Queue - if it's being used from multiple threads. An alternative, if you're using .NET 4, would be to change over to using the new ConcurrentQueue(Of T) class instead of Queue. If you use SyncLock, you should create a private object only accessible by your class, and use it for all synchronization.

You should be using the Interlocked class here, the Decrement() method to decrease the count. Yes, everywhere the variable is accessed.
Using SyncLock Me is as bad as SyncLock GetType(Me). You should always use a private object to lock on so nobody can accidentally cause a deadlock. The golden rule is that you cannot lock data, you can only block code from accessing data. Since the code is your private implementation detail, the object that holds the lock state must also be a private detail. Neither your object (Me) nor the Type of that object is private. Allowing other code to lock it by accident.

You can substitute the thread counter with Semaphore. If you use Semaphore you do not need to exit from while loop and neither it is necessary to call WorkThroughQueue() from ThreadCompleted event handler. Semaphore is thread safe so you can use it without locking.
http://www.albahari.com/threading/part2.aspx#_Semaphore

Related

VB.Net: timer and event handler influence each other

In a Windows form I have:
a timer tmrBackup that regularly performs a backup of a local database
an event procedure that raises a procedure ImportNewFile when FileSystemWatcher detects a new file in a given directory.
Each of these work perfectly as long as they don't interfere each other.
I try to prevent that by stopping the FileSystemWatcher when a backup starts, and starting it again when it's finished.
On the other hand I set tmrBackup.Stop() in the beginning of ImportNewFile, then the contents of the file are transformed into a record in the local database. At the end of ImportNewFile I set tmrBackup.Start() again. But the timer is never started again, so that seems to be not a viable way.
When I don't set tmrBackup.Stop(), though, I see a strange behavior during debugging: when somewhere in ImportNewFile the timer fires, it seems that both routines are running parallel: one line in the tmrBackup routine is executed, then VB.Net jumps back to ImportNewFile for one line, then back to the timer, etc.
Question: what is a correct method to work with two objects with each being able to fire while the other's event is handled? Thanks!
Rather than attempting to temporarily disable the opposite timer, which is hokey and not guaranteed to work, the typical solution in such cases is to put SyncLock blocks around the necessary code in each of the event handlers, and have them both synchronize to the same object.
When code is surrounded by a SyncLock, execution will stop and wait, before entering the SyncLock block until any other related SyncLock code is finished. The way to relate them is by giving the SyncLock statement an object to synchronize against. If two SyncLock blocks synchronize to the same object, then neither of those two blocks of code will ever be allowed to run simultaneously.
In other words, if both blocks of code get called simultaneously, whichever starts first wins, and it runs through until it's complete, and the second just waits to begin executing until the first one is done.
Public Class SyncLockExample
Private WithEvents object1 As MyType1
Private WithEvents object2 As MyType2
Private syncObj As New Object()
Private Sub object1_MyEvent1(Object sender, EventArgs e) Handles object1.MyEvent1
SyncLock syncObject
' Do something that can't happen at the same time as object2_MyEvent2
End SyncLock
End Sub
Private Sub object2_MyEvent2(Object sender, EventArgs e) Handles object2.MyEvent2
SyncLock syncObject
' Do something that can't happen at the same time as object1_MyEvent1
End SyncLock
End Sub
End Class

using locks to control access to non thread safe libraries

I'm working on converting a single threaded application to be a multi threaded one.
One of the libraries we use, according to their documentation is not thread safe. So to get around this the answer is to use locks (at least that's my understanding so far..)
So I kick off my threads:
Parallel.ForEach(recordList, Sub(record) ProcessRecord(record))
Public Sub ProcessRecord(ByVal record As UploadRecord)
Dim controller As New UploadController()
controller.ProcessRecord(record)
End Sub
In the class UploadController(), I've added a lock object.
private thisLock As Object = new Object()
I then use this lock to control access to calls to my non-thread safe library.
SyncLock (thisLock)
structureTemplatefs = IO.File.OpenRead(GetVersion(record, validationTemplatePathElement) & structureTemplateElement)
validator = New FileValidator(structureTemplatefs, record.FileData, True)
'Get all errors...
result = validator.Validate(FileValidator.ValidationEngineType.Structure, True)
End SyncLock
AND:
SyncLock thisLock
record.FileData.Seek(0, SeekOrigin.Begin)
cellValue = excelUtility.GetCellFormulaValue(record.FileData, sheetNo, GetConfigSetting(GenericConfigSection.VALIDATION_CELL_RANGE_NAME), fileName)
End SyncLock
However, its not working. Is it because each thread creates its own instance of UploadController() and therefore there are multiple locks?
On further reading of the following:
https://msdn.microsoft.com/en-us/library/3a86s51t.aspx
In particular the following:
Protected Data. If lockobject is a Shared variable, the exclusive
lock prevents a thread in any instance of the class from executing the
SyncLock block while any other thread is executing it. This protects
data that is shared among all the instances.
If lockobject is an instance variable (not Shared), the lock prevents
a thread running in the current instance from executing the SyncLock
block at the same time as another thread in the same instance. This
protects data maintained by the individual instance.
As a result I've modified my lock to be Shared.
This appears to have resolved my issue.

Is double synclock within the same thread ok?

I have a situation where multiple threads may access a class, the class consists of 3 private values and 6 properties that get/set various arithmetic combinations of the 3 values. Then I have 6 more properties that are just type conversions as they call on the first 6 properties as a part of their get/set operation.
All the get/set functions of the first 6 properties are synclocked to the same object. I wanted to synclock the get/set functions of the other 6 too to the same object since they use some of the same utility objects but it occurred to me that this will lead to synclocking the same object twice in a row.. sorta like this:
Private lock As New Object
Private _dCool As Double
Public Property dCool As Double
Get
SyncLock lock
Return _dCool
End SyncLock
End Get
Set(value As Double)
SyncLock lock
_dCool = value
End SyncLock
End Set
End Property
Public Property iCool As Integer
Get
SyncLock lock
Return dCool
End SyncLock
End Get
Set(value As Integer)
SyncLock lock
dCool = value
End SyncLock
End Set
End Property
There is no need to synclock iCool in this example but its just to illustrate the problem. As far as I've tested, there seem to be no problems with this but just wanted to ask if some problems i cant see right now could come up by doing this?
Yes, not a problem. The VB.NET compiler generates calls to Monitor.Enter/Exit() when you use the SyncLock keyword, along with Try/Finally blocks to ensure that the entered monitor is always exited. The MSDN article for Monitor.Enter says:
It is legal for the same thread to invoke Enter more than once without it blocking; however, an equal number of Exit calls must be invoked before other threads waiting on the object will unblock.
Relevant phrase bolded. The Finally block ensure that the Exit() call requirement is always met.

VB.NET multithreading, block thread until notification received

Before I begin, I have to apologize for two things. One is that it is very difficult for me to explain things in a concise manner. Two is that I need to be somewhat vague due to the nature of the company I work for.
I am working on enhancing the functionality of an application that I've inherited. It is a very intensive application that runs a good portion of my company's day to day business. Because of this I am limited to the scope of what I can change--otherwise I'd probably rewrite it from scratch. Anyways, here is what I need to do:
I have several threads that all perform the same task but on different data input streams. Each thread interacts through an API from another software system we pay licensing on to write out to what is called channels. Unfortunately we have only licensed a certain number of concurrently running channels, so this application is supposed to turn them on an off as needed.
Each thread should wait until there is an available channel, lock the channel for itself and perform its processing and then release the channel. Unfortunately, I don't know how to do this, especially across multiple threads. I also don't really know what to search Google or this site for, or I'd probably have my answer. This was my thought:
A class that handles the distribution of channel numbers. Each thread makes a call to a member of this class. When it does this it would enter a queue and block until the channel handling class recognizes that we have a channel, signals the waiting thread that a channel is available and passing it the channel id. I have no idea where to begin even looking this up. Below I have some horribly written PsuedoCode of how in my mind I would think it would work.
Public Class ChannelHandler
Private Shared WaitQueue as New Queue(of Thread)
'// calling thread adds itself to the queue
Public Shared Sub WaitForChannel(byref t as thread)
WaitQueue.enqueue(t)
End Sub
Public Shared Sub ReleaseChannel(chanNum as integer)
'// my own processing to make the chan num available again
End Sub
'// this would be running on a separate thread, polling my database
'// for an available channel, when it finds one, somehow signal
'// the first thread in the queue that its got a channel and here's the id
Public Shared Sub ChannelLoop()
while true
if WaitQueue.length > 0 then
if thereIsAChannelAvailable then '//i can figure this out my own
dim t as thread = ctype(WaitQueue.dequeue(), Thread)
lockTheChannel(TheAvailableChannelNumber) 'performed by me
'// signal the thread, passing it the channel number
t => SignalReady(theAvailableChannelNumber) '// how to signal?
end if
end if
end while
End Sub
End Class
and then
'// this inside the function that is doing the processing:
ChannelHandler.requestChannel(CurrentThread)
while (waitingForSignal) '// how?
block '// how?
dim channelNumber as int => getChannelNumberThatWasSignaledBack
'// perform processing with channelNumber
ChannelHandler.ReleaseChannel(channelNumber)
I am working with the .NET Framework 3.5 in VB.NET. I am sure there has got to be some sort of mechanism already built for this, but as I said I have no idea exactly what keywords I should be searching for. Any input pointing me in the right direction (ie specific .NET framework classes to use or code samples) would be greatly appreciated. If I need to elaborate on anything, please let me know and I will to the best of my ability.
Edit: The other problem that I have is that these channels can be turned on/off from outside of this application, manually by the user (or as a result of a user initiated event). I am not concerned with a channel be shut down while a thread is using it (it would throw an exception and then pick back up next time it came through. But the issue is that there are not a constant number of threads fighting over a constant number of channels (if a user turns one on manually, the count is reduced, etc). Both items are variable, so I cant rely on the fact that there are no external forces (ie, something outside this set of threads, which is why I do some processing via my DB to determine an available channel number)
What I would do:
Switch the System.Threading.Thread by the System.Threading.Tasks.Task class.
If a new Task needs to be created, but the List(Of Task) (or, in your example, Queue(Of Task) ) count greater than the maximum permitted, use the Task.WaitAny method.
EDIT:
As I answered the previous block on my phone (which is pretty challenging for writing code), let now me write an example about how I would do it:
Imports System.Threading.Tasks
Imports System.Collections.Generic
Public Class Sample
Private Const MAXIMUM_PERMITTED As Integer = 3
Private _waitQueue As New Queue(Of Task)
Public Sub AssignChannel()
Static Dim queueManagerCreated As Boolean
If Not queueManagerCreated Then
Task.Factory.StartNew(Sub() ManageQueue())
queueManagerCreated = True
End If
Dim newTask As New Task(Sub()
' Connect to 3rd Party software
End Sub)
SyncLock (_waitQueue)
_waitQueue.Enqueue(newTask)
End SyncLock
End Sub
Private Sub ManageQueue()
Dim tasksRunning As New List(Of Task)
While True
If _waitQueue.Count <= 0 Then
Threading.Thread.Sleep(10)
Continue While
End If
If tasksRunning.Count > MAXIMUM_PERMITTED Then
Dim endedTaskPos As Integer = Task.WaitAny(tasksRunning.ToArray)
If endedTaskPos > -1 AndAlso
endedTaskPos <= tasksRunning.Count Then
tasksRunning.RemoveAt(endedTaskPos)
Else
Continue While
End If
End If
Dim taskToStart As Task
SyncLock (_waitQueue)
taskToStart = _waitQueue.Dequeue()
End SyncLock
tasksRunning.Add(taskToStart)
taskToStart.Start()
End While
End Sub
End Class

Should I always lock static methods?

In the following example GetList returns an instance of a static (shared) variable. That one needs locking in order to be thread-safe.
But what about DoSomething which doesn't use any static variables outside the method? Does it need locking too?
EDIT: I'd like to clarify that in this particular case I expect DoSomething to always print 0-100 in sequence (i.e. no 0123745...) regardless of number of calling threads. Or, in general, that different threads don't affect each other's variables (printing to console is only an example). The language in the following example is VB.NET.
As paxdiablo said:
In this case, it appears the only thing touched is the local variable
i which would have a separate copy for every function invocation.
In other words, it wouldn't need protecting.
That is exactly what I was trying to solve. Thank you!
Public Class TestClass
Private Shared lock As New Object
Private Shared list As List(Of Integer)
Public Shared Function GetList() As List(Of Integer)
SyncLock lock
If list Is Nothing Then
list = New List(Of Integer)
End If
Return list
End SyncLock
End Function
Public Shared Sub DoSomething()
Dim i As Integer
For i = 0 To 100
Console.WriteLine(i.ToString)
Next
End Sub
End Class
Well, that would mostly depend on the language which you haven't specified but, generally, if code doesn't touch a resource that another thread can also touch, it doesn't have to be protected.
In this case, it appears the only thing touched is the local variable i which would have a separate copy for every function invocation. In other words, it wouldn't need protecting.
Of course, it could be argued that the console is also a resource and may need protection if, for example, you didn't want lines interfering with each other (synclock the write) or wanted the entire hundred lines output as a single unit (synclock the entire for loop).
But that won't really protect the console, just the block of code here that uses it. Other threads will still be able to write to the console by not using this method.
Bottom line, I don't think you need a synclock in the second method.
This section below is not relevant if you're using SyncLock in VB.Net (as now seems to be the case). The language guarantees that the lock is released no matter how you leave the block. I'll leave it in for hysterical purposes.
I'd be a little concerned about your synclock placement in the first method, especially if the return statement was a transfer of control back to the caller (and the synclock didn't automatically unlock on scope change). This looks like you can return without unlocking, which would be a disaster. I would think the following would be more suitable:
Public Shared Function GetList() As List(Of Integer)
SyncLock lock
If list Is Nothing Then
list = New List(Of Integer)
End If
End SyncLock
Return list
End Function
In general, no.
You need to be clear on the reason why GetList has the locking applied. It's not, as you imply in your first sentence, because it's returning a static variable. You could remove the locking code, and GetList would still be thread safe. But, with the locking, there's an additional guarantee - that the list will only be created once, and all callers of this code will receive a reference to the same list.
Why not avoid the lock altogether and just do this:
Public Class TestClass
Private Shared lock As New Object
Private Shared list As New List(Of Integer)
Public Shared Function GetList() As List(Of Integer)
Return list
End Function
Public Shared Sub DoSomething()
Dim i As Integer
For i = 0 To 100
Console.WriteLine(i.ToString)
Next
End Sub
End Class