What's the proper use of WaitOne() function - vb.net

I was experimented some Thread pool examples. I've started from Fibonacci example on MSDN web site, but this wasn't suitable for more than 64 calculations, so i've resolved with this code:
Imports System.Threading
Module Module1
Public Class Fibonacci
Private _n As Integer
Private _fibOfN
Private _doneEvent As ManualResetEvent
Public ReadOnly Property N() As Integer
Get
Return _n
End Get
End Property
Public ReadOnly Property FibOfN() As Integer
Get
Return _fibOfN
End Get
End Property
Sub New(ByVal n As Integer, ByVal doneEvent As ManualResetEvent)
_n = n
_doneEvent = doneEvent
End Sub
' Wrapper method for use with the thread pool.
Public Sub ThreadPoolCallBackMar(ByVal threadContext As Object)
Dim threadIndex As Integer = CType(threadContext, Integer)
Console.WriteLine("thread {0} started...", threadIndex)
_fibOfN = Calculate(_n)
Console.WriteLine("thread {0} result calculated...", threadIndex)
_doneEvent.Set()
End Sub
Public Function Calculate(ByVal n As Integer) As Integer
If n <= 1 Then
Return n
End If
Return Calculate(n - 1) + Calculate(n - 2)
End Function
End Class
<MTAThread()>
Sub Main()
Const FibonacciCalculations As Integer = 65
' One event is used for each Fibonacci object
Dim doneEvents(FibonacciCalculations) As ManualResetEvent
Dim fibArray(FibonacciCalculations) As Fibonacci
Dim r As New Random()
' Configure and start threads using ThreadPool.
Console.WriteLine("launching {0} tasks...", FibonacciCalculations)
For i As Integer = 0 To FibonacciCalculations
doneEvents(i) = New ManualResetEvent(False)
Dim f = New Fibonacci(r.Next(20, 40), doneEvents(i))
fibArray(i) = f
ThreadPool.QueueUserWorkItem(AddressOf f.ThreadPoolCallBackMar, i)
Next
Console.WriteLine("All calculations are complete.")
For i As Integer = 0 To FibonacciCalculations
doneEvents(i).WaitOne()
Dim f As Fibonacci = fibArray(i)
Console.WriteLine("Fibonacci({0}) = {1}", f.N, f.FibOfN)
Next
Console.Read()
End Sub
End Module
The use of WaitOne() instead of WaitAll() resolve the problem but the question is: If I don't need to display the results then I don't need neither the second loop, but... without the second loop where I've to put the waitOne() function?

Your code does essentially this:
// start a bunch of threads to do calculations
Console.WriteLine("All calculations are complete."); // This is a lie!
// Wait for the threads to exit
The primary problem here is that the calculations are not complete when you make that call to Console.WriteLine. Well, they might be complete, but you don't know unless you've waited on the event to see that it's signaled.
The purpose of WaitOne is to tell you if the calculation has completed. Your code should be written like this:
For i As Integer = 0 To FibonacciCalculations
doneEvents(i) = New ManualResetEvent(False)
Dim f = New Fibonacci(r.Next(20, 40), doneEvents(i))
fibArray(i) = f
ThreadPool.QueueUserWorkItem(AddressOf f.ThreadPoolCallBackMar, i)
Next
Console.WriteLine("All calculations are started. Waiting for them to complete.")
For i As Integer = 0 To FibonacciCalculations
doneEvents(i).WaitOne()
Dim f As Fibonacci = fibArray(i)
Console.WriteLine("Fibonacci({0}) = {1}", f.N, f.FibOfN)
Next
Console.WriteLine("All calculations are complete.")
You must check the event to know that the calculation is complete.
Now, if you don't need to know if the calculation is complete, then there's no need for the WaitOne at all. And if you're not going to wait on the event, then there's no real need to have the event, is there? Although one wonders why you're going to do a calculation and then not use the result.

Related

Summing up decimals returned within task

I'm trying to run through a list of 1-X items and process them in Tasks in order to speed up completion of processing (they make web requests that can sometimes take a short while to finish). I'm trying to sum up a decimal that is returned when the processing is finished without looping through the list a second time. As it is right now, the sum is overwritten by each task instead of being added to.
I can insert a short pause before adding the decimal to the sum and this seems to work fine, but I'm sure there is a better solution out there.
I've seen mentions of SyncLock but I can't use it on a Decimal.
Here is some code I wrote for testing. The .Process method just waits a random number of seconds and sets the .Value property to a random decimal.
Any suggestions for improvement and calculating the sum in a proper way?
Try
Dim lstItem As List(Of clsItem) = GetList()
Dim lstViewItem As New List(Of ListViewItem)
Dim lstTasks As New List(Of Task)()
Dim decTotal As Decimal = 0.0
For i As Int32 = 0 To lstItem.Count - 1
Dim j As Int32 = i
Dim t As Task = Task.Run(Sub()
lstViewItem.Add(New ListViewItem(New String() {j, lstItem(j).Process()}))
'System.Threading.Thread.Sleep(j * 1000)
decTotal += lstItem(j).Value
End Sub)
lstTasks.Add(t)
Next
Task.WaitAll(lstTasks.ToArray())
lstView.Items.AddRange(lstViewItem.ToArray())
lstView.Items.Add("")
lstView.Items.Add(decTotal)
Catch ex As Exception
Debug.WriteLine(ex.Message)
End Try
You should be able to use SyncLock to resolve your issue. SyncLock can only be used on reference types. All that's needed is to create a new object that is within the scope of the current instance and then let SyncLock lock on that object.
Here's a short little console application that utilizes SyncLock. I significantly simplified what you had. I didn't really feel like making listViews and such, and rather than returning a random decimal it just returns 1 so that the results should be the number of loop iterations.
Option Strict On
Option Explicit On
Imports System.Threading.Tasks
Module Module1
Sub Main()
Dim lstTasks As New List(Of Task)()
Dim SyncLockedTotal As Decimal = 0D
Dim decTotal As Decimal = 0D
Dim sLock As New Object
For i As Int32 = 1I To 1000I
Dim t As Task = Task.Run(Sub()
Dim tempDecimal As Decimal = clsItem.Process()
decTotal += tempDecimal
SyncLock sLock
SyncLockedTotal += tempDecimal
End SyncLock
End Sub)
lstTasks.Add(t)
Next
Task.WaitAll(lstTasks.ToArray())
Console.WriteLine(String.Format("Value for decTotal: {0}", decTotal.ToString))
Console.WriteLine(String.Format("Value for SyncLockedTotal: {0}", SyncLockedTotal.ToString))
Console.ReadLine()
End Sub
End Module
Public Structure clsItem
Public Shared Function Process() As Decimal
Dim rnd As New Random(DateTime.Now.Millisecond)
Threading.Thread.Sleep(rnd.Next(10I))
Return 1D
End Function
End Structure
NOTE: Your application settings must target .NET 4.5 in order to use Task.Run(), if you are targetting .NET 4 you will have to use Task.Factory.StartNew(). If you are targeting .NET 3.5 or lower, this won't work for you because you won't have the System.Threading.Tasks namespace available.

Local Dim vs. Static variable

From the IL perspective, what are the main differences (performance wise) between a local Dim variable and a local Static variable?
I've always thought that Dim would allocate storage space every time, whilst Static would allocate storage space only once, hence faster. But as you can see below, this is not the case.
A: Dim
B: Static
Public Class Form1
Public Sub New()
Me.InitializeComponent()
Dim b As New Button() With {.Text = "Run", .Height = 30, .Location = New Point(10, 10)}
AddHandler b.Click, AddressOf Me.Run
Me.Controls.Add(b)
End Sub
Private Sub Run(sender As Object, e As EventArgs)
Dim count As Integer = 10000
Dim watch As New Stopwatch()
Dim list As New Test(Of Control)(count)
Dim last As Control = list.Items(count - 1)
Dim a, b As Double, i As Integer
For i = 1 To 10000
watch.Restart()
list.IndexOfA(last)
a += watch.Elapsed.TotalMilliseconds
Next
For i = 1 To 10000
watch.Restart()
list.IndexOfB(last)
b += watch.Elapsed.TotalMilliseconds
Next
watch.Stop()
Array.ForEach(Of Control)(list.Items, Sub(c As Control) c.Dispose())
list = Nothing
MessageBox.Show(String.Format("A: {0}{1}B: {2}", a.ToString("F4"), Environment.NewLine, b.ToString("F4")))
End Sub
Public Class Test(Of T As {Class, New})
Public Sub New(count)
If (count < 0) Then Throw New ArgumentOutOfRangeException("count")
Dim items As T() = New T(count - 1) {}
For index As Integer = (count - 1) To 0 Step -1
items(index) = New T()
Next
Me.Items = items
End Sub
Public Function IndexOfA(item As T) As Integer
Dim index As Integer
Dim length As Integer
Dim item2 As T
length = (Me.Items.Length - 1)
For index = 0 To length
item2 = Me.Items(index)
If (item2 Is item) Then
Return index
End If
Next
Return -1
End Function
Public Function IndexOfB(item As T) As Integer
Static index As Integer
Static length As Integer
Static item2 As T
length = (Me.Items.Length - 1)
For index = 0 To length
item2 = Me.Items(index)
If (item2 Is item) Then
Return index
End If
Next
Return -1
End Function
Public ReadOnly Items As T()
End Class
End Class
Edit
As per comment, I've edited the code so it don't "restart the stopwatch at every loop iteration".
watch.Start()
For i = 1 To 10000
list.IndexOfA(last)
Next
watch.Stop()
a = watch.Elapsed.TotalMilliseconds
watch.Restart()
For i = 1 To 10000
list.IndexOfB(last)
Next
watch.Stop()
b = watch.Elapsed.TotalMilliseconds
The results are almost the same:
A: 358,3624
B: 538.8570
The main difference in performance is not how the variables are allocated, it's in how they are accessed.
Local variables are allocated on the stack, which takes no time at all. Literally. The allocation is done by moving the stack pointer, and that is done anyway to create a stack frame for the method, so allocating local variables doesn't take any more time.
Static variables in an instance method are allocated as part of the data for the class, and that is done only once in this case. Allocating another variable only means that more data is allocated, so that doesn't increase the time.
Accessing the variables is a different story. A local variable is accessed by addressing part of the stack frame, which is easy. A static variable on the other hand is accessed by getting a pointer to the static data of the class, and then addressing the variable at an offset from that. That means that every access to a static variable needs a few more instructions.
VB local static variables are allocated once per instance if they occur within an instance method. They are only allocated once overall if they occur within a Shared method.

Different Results using Parallel foreach everytime Excel Worksheet is read

Imports System.IO
Imports System.Threading
Imports System.Threading.Tasks
Imports System.Collections.Concurrent
Imports Excel = Microsoft.Office.Interop.Excel
Public Class TestCarDatas
Public Property RowID As Integer
Public Property ModelYear As Integer
Public Property VehMfcName As String
Public Property EmgVeh As Boolean
End Class
Module ExcelParallelDataGather2
Public Const ExcelVehDataPath As String = "D:\Users\Dell\Desktop"
Public rwl As New System.Threading.ReaderWriterLock()
Public rwl_writes As Integer = 0
Public FullTestVehData As New List(Of TestCarDatas)()
Public x1App As New Excel.Application
Public x1Workbook As Excel.Workbook
Public x1Worksheet1 As Excel.Worksheet
Public x1WkshtLrow As Integer
Sub main()
x1App.Visible = False
ErrorNotify = False
Console.WriteLine("Excel Parallel foreach operation program....")
Dim cki As ConsoleKeyInfo
x1Workbook = x1App.Workbooks.Open(Path.Combine(ExcelVehDataPath, "TestCarDatabase2014.xls"), , True)
x1Worksheet1 = x1Workbook.Sheets(1)
Do
Console.WriteLine("Press escape key to exit, 'a' key to reiterate, 'c' key to clear console")
cki = Console.ReadKey()
If Chr(cki.Key).ToString = "A" Then
Console.WriteLine("--> Processing...")
FullTestVehData.Clear()
rwl_writes = 0
Parallel.ForEach(Partitioner.Create(11, x1WkshtLrow + 1), _
Function()
' Initialize the local states
Return New List(Of TestCarDatas)()
End Function, _
Function(partrange, loopState, localState)
' Accumulate the thread-local computations in the loop body
localState = populateCardata(x1Worksheet1, partrange.Item1, partrange.Item2)
Return (localState)
End Function, _
Sub(finalstate)
' Combine all local states
Try
rwl.AcquireWriterLock(Timeout.Infinite)
Try
' It is safe for this thread to read or write
' from the shared resource in this block
FullTestVehData.AddRange(finalstate)
Interlocked.Increment(rwl_writes)
Finally
' Ensure that the lock is released.
rwl.ReleaseWriterLock()
End Try
Catch ex As ApplicationException
' The writer lock request timed out.
End Try
End Sub)
End If
If Chr(cki.Key).ToString = "C" Then
Console.Clear()
Console.WriteLine("Excel Parallel foreach operation program....")
End If
If Chr(cki.Key).ToString <> "A" And Chr(cki.Key).ToString <> "C" And _
cki.Key <> ConsoleKey.Escape Then
Console.WriteLine("")
Console.WriteLine("Invalid response via key press")
End If
Loop While (cki.Key <> ConsoleKey.Escape)
End Sub
Friend Function populateCardata(ByVal WksheetObj As Excel.Worksheet, ByVal rngStart As Integer, _
ByVal rngStop As Integer) As List(Of TestCarDatas)
Dim wkrng(12) As String
Dim PartVehData As New List(Of TestCarDatas)
PartVehData.Clear()
For i As Integer = rngStart To rngStop - 1
Dim data As New TestCarDatas
For j As Integer = 0 To 12
wkrng(j) = WksheetObj.Cells(i, j + 1).Address(RowAbsolute:=False, ColumnAbsolute:=False)
Next
With data
.RowID = i
.ModelYear = WksheetObj.Range(wkrng(0)).Value2
.VehMfcName = WksheetObj.Range(wkrng(1)).Value2
If WksheetObj.Range(wkrng(11)).Value2 = "Y" Then
.EmgVeh = True
Else
.EmgVeh = False
End If
End With
PartVehData.Add(data)
Next
Return PartVehData
End Function
End Module
I am trying to get the Excel Worksheet data using parallel foreach and range Partitioner, creating lists in thread local storage and finally adding them using thread safe methods like synclock or reader-writer locks
Rows from 11 to last row in worksheet are to be read and populated in a List(of T)
I observe following when I execute above code
When rows in worksheet are greater(example >2000), this code works as expected everytime
When rows in worksheet are less, this code returns partial list during first few iteration (data from some of the partitioner ranges are lost). If I re-iterate it (pressing key 'a') multiple times, then sometimes it returns expected results (final list count = no. of excel rows required to be read)
Why this phenomenon is observed?
What is the solution using parallel foreach, if I need correct results during first run/iteration irrespective of no. of rows in worksheet?

Handle more than 64 thread at the same time

I was reading a tutorial about Thread pooling in VB. There was an example with Fibonacci calculations:
Imports System.Threading
Module Module1
Public Class Fibonacci
Private _n As Integer
Private _fibOfN
Private _doneEvent As ManualResetEvent
Public ReadOnly Property N() As Integer
Get
Return _n
End Get
End Property
Public ReadOnly Property FibOfN() As Integer
Get
Return _fibOfN
End Get
End Property
Sub New(ByVal n As Integer, ByVal doneEvent As ManualResetEvent)
_n = n
_doneEvent = doneEvent
End Sub
' Wrapper method for use with the thread pool.
Public Sub ThreadPoolCallBack(ByVal threadContext As Object)
Dim threadIndex As Integer = CType(threadContext, Integer)
Console.WriteLine("thread {0} started...", threadIndex)
_fibOfN = Calculate(_n)
Console.WriteLine("thread {0} result calculated...", threadIndex)
_doneEvent.Set()
End Sub
Public Function Calculate(ByVal n As Integer) As Integer
If n <= 1 Then
Return n
End If
Return Calculate(n - 1) + Calculate(n - 2)
End Function
End Class
<MTAThread()>
Sub Main()
Const FibonacciCalculations As Integer = 9 ' 0 to 9
' One event is used for each Fibonacci object
Dim doneEvents(FibonacciCalculations) As ManualResetEvent
Dim fibArray(FibonacciCalculations) As Fibonacci
Dim r As New Random()
' Configure and start threads using ThreadPool.
Console.WriteLine("launching {0} tasks...", FibonacciCalculations)
For i As Integer = 0 To FibonacciCalculations
doneEvents(i) = New ManualResetEvent(False)
Dim f = New Fibonacci(r.Next(20, 40), doneEvents(i))
fibArray(i) = f
ThreadPool.QueueUserWorkItem(AddressOf f.ThreadPoolCallBack, i)
Next
' Wait for all threads in pool to calculate.
WaitHandle.WaitAll(doneEvents)
Console.WriteLine("All calculations are complete.")
' Display the results.
For i As Integer = 0 To FibonacciCalculations
Dim f As Fibonacci = fibArray(i)
Console.WriteLine("Fibonacci({0}) = {1}", f.N, f.FibOfN)
Next
End Sub
End Module
I've start this module and it works correctly and this just handle 9 calculations:
Const FibonacciCalculations As Integer = 9
I've increase that limits, but this can just handle up to 63 calculations. From 64th calculation exception is raised that said:
waithandle must be less than 64
I would this application will handle N calculations. A good idea could be set a cap to pool of threads (for instance: 6). The N calculations will be handle using at most 6 thread at once. How could I edit the code to handle this removing the waitHandle error?
The winapi restriction on the number of handles you can wait on at the same time is a pretty hard one. It is just not necessary, you'll get the exact same outcome if you wait for each individual one:
' Wait for all threads in pool to calculate.
For i As Integer = 0 To FibonacciCalculations
doneEvents(i).WaitOne()
Next
And note how you can now combine this with the next loop, making your program more efficient since you overlap the calculation with the display. So you really want to favor this instead:
' Display the results.
For i As Integer = 0 To FibonacciCalculations
doneEvents(i).WaitOne()
Dim f As Fibonacci = fibArray(i)
Console.WriteLine("Fibonacci({0}) = {1}", f.N, f.FibOfN)
Next
If you want to wait for X task completions where X>63, use a countdown: ONE atomic int and ONE MRE. Initialize the int to [no. of tasks], start your tasks and wait on the MRE with WaitForSingleObject() / WaitOne(). When a task is complete, it atomically decrements the int. Any task, running on any thread, that decrements it to zero signals the MRE.
In fact, use this mechanism for X<63 :)
There is no real advantage to using that many threads on a machine with 4 cores or 2 cores. You really, ideally, only want the same number of threads as you have cores.
If you have more you start losing on parallelism as threads need to be context switched out to let others run. You also may run into contention issues, depending on how your algorithm is written.
The point of a threadpool is really to tell your system to maximally use some number of threads and leave the rest up to the system to figure out what is best.
It may be that your system cannot support more than 64 objects in WaitHandle, see here:
http://msdn.microsoft.com/en-us/library/z6w25xa6.aspx
You can find a workaround for this issue here:
Workaround for the WaitHandle.WaitAll 64 handle limit?
However, as the other answers state, you probably would not gain from having this many threads anyway.

.NET Terminating Threads in an orderly fashion

Currently, I have a RingBuffer which is run by a producer and a consumer thread.
In looking for a method of terminating them orderly, I thought I'd use a flag to indicate when the producer had finished and then check that flag in my consumer along with the number of ring buffer slots that need to be written. If the producer has finished and the ring buffer has no slots that need to be written the consumer can terminate.
That works well.
However, if I artificially lengthen the time the producer takes by inserting a sleep, the consumer does not terminate. I believe this is a consequence of the semaphores being used.
Here is the code I'm working with. Notice that the program will "hang" after all slots have been written. The producer terminates, but the consumer "hangs".
Any advice on terminating both in an orderly fashion would be greatly appreciated.
Edit - Updated code with Henk's suggestion of using a Queue. +1000 points to the first person to suggest a better method of terminating the consumer/producer threads than either knowing the exact amount of items being worked with or returning a value such as null/nothing indicating that no more items exist in the queue (though this doesn't mean they aren't still being produced.)
Edit - I believe I've figured it out. Simply pass null or nothing to RingBuffer.Enqueue for each consumer and catch the null or nothing object in the consumer to terminate it. Hopefully someone finds this useful.
Imports System.Collections
Module Module1
Public Class RingBuffer
Private m_Capacity As Integer
Private m_Queue As Queue
Public Sub New(ByVal Capacity As Integer)
m_Capacity = Capacity
m_Queue = Queue.Synchronized(New Queue(Capacity))
End Sub
Public Sub Enqueue(ByVal value As Object)
SyncLock m_Queue.SyncRoot
If m_Queue.Count = m_Capacity Then
Threading.Monitor.Wait(m_Queue.SyncRoot)
End If
m_Queue.Enqueue(value)
Threading.Monitor.PulseAll(m_Queue.SyncRoot)
End SyncLock
End Sub
Public Function Dequeue() As Object
Dim value As Object = Nothing
SyncLock m_Queue.SyncRoot
If m_Queue.Count = 0 Then
Threading.Monitor.Wait(m_Queue.SyncRoot)
End If
value = m_Queue.Dequeue()
Console.WriteLine("Full Slots: {0} - Open Slots: {1}", m_Queue.Count, m_Capacity - m_Queue.Count)
Threading.Monitor.PulseAll(m_Queue.SyncRoot)
End SyncLock
Return value
End Function
End Class
Public Class Tile
Public buffer() As Byte
Public Sub New()
buffer = New Byte(1023) {}
End Sub
End Class
Public Sub Producer(ByVal rb As RingBuffer)
Dim enq As Integer = 0
Dim rng As New System.Security.Cryptography.RNGCryptoServiceProvider
For i As Integer = 0 To 1023
Dim t As New Tile
rng.GetNonZeroBytes(t.buffer)
rb.Enqueue(t)
enq += 1
Threading.Thread.Sleep(10)
Next i
rb.Enqueue(Nothing)
Console.WriteLine("Total items enqueued: " & enq.ToString())
Console.WriteLine("Done Producing!")
End Sub
Public Sub Consumer(ByVal rb As RingBuffer)
Dim deq As Integer = 0
Using fs As New IO.FileStream("c:\test.bin", IO.FileMode.Create)
While True
Dim t As Tile = rb.Dequeue()
If t Is Nothing Then Exit While
fs.Write(t.buffer, 0, t.buffer.Length)
deq += 1
Threading.Thread.Sleep(30)
End While
End Using
Console.WriteLine("Total items dequeued: " & deq.ToString())
Console.WriteLine("Done Consuming!")
End Sub
Sub Main()
Dim rb As New RingBuffer(1000)
Dim thrdProducer As New Threading.Thread(AddressOf Producer)
thrdProducer.SetApartmentState(Threading.ApartmentState.STA)
thrdProducer.Name = "Producer"
thrdProducer.IsBackground = True
thrdProducer.Start(rb)
Dim thrdConsumer As New Threading.Thread(AddressOf Consumer)
thrdConsumer.SetApartmentState(Threading.ApartmentState.STA)
thrdConsumer.Name = "Consumer"
thrdConsumer.IsBackground = True
thrdConsumer.Start(rb)
Console.ReadKey()
End Sub
End Module
If I look at the Consumer function:
If rb.FullSlots = 0 And Threading.Interlocked.Read(ProducerFinished) = 0 Then
Exit While
End If
Dim t As Tile = rb.Read()
The consumer could find rb.FullSlots = 0 but ProducerFinished = False and continue to Read(). Inside Read() it waits for the writerSemaphore but in the mean time the Producer could finish and never release the writerSemaphore.
So (at least) the producer should take steps to let the readers continue after it decreases the ProducerFinished.
But I think you get a better design if you move this 'Closing' logic to the Ring buffer. There you can combine it with the Data-available logic.