Return once condition is true without hanging - vb.net

So I have this computation job, that requires startng 6 threads and waiting for those to finish. The threads change a "local" variable within the class.
I want to have the function return "True" once the local variable is a certain value. However, I want to do this in a fashion where it doesn't hang the thread. So a constant "Do Loop" is not gonna work. Are there any standard ways of doing this?
Public Function Start(ByVal Cores As Integer) As Boolean
For i = 0 To 10
// Heavy work
Task.Factory.StartNew(Sub() Compute(Core, StartInt, EndInt))
Next
Do // <- How to avoid checking ThreadsTerimnated = ThreadsStarted every clockcycle?
// Threading.Sleep hangs thread.
If ThreadsTerminated = ThreadsStarted Then
MergeResults(Cores)
Return True
End If
Loop
End Function

You can keep a list of your Tasks and use Tasks.WaitAll
Dim tasks As New List(Of Task)
For i = 0 To 10
// Heavy work
tasks.Add(Task.Factory.StartNew(Sub() Compute(Core, StartInt, EndInt)))
Next
Task.WaitAll(tasks.ToArray())

Related

Unexpected results when using `Task.Run` to call a synchronous method

I'm working on an application where at some point i'm doing some CPU intensive calculation on the kinematics of an object. At some point i'm using Task.Run() to call the function doing the work, this leads to some unexpected results, i'm not sure if this would be considered a race condition or if it has some other name. My real code is rather expansive, so i have reduced the issue to what i believe to be a minimal working example, to be run in a .net framework console application.
For the MWE consider the following class, it has 3 fields and a constructor initializing them. It also has a report() sub for easier debugging.
Public Class DebugClass
Public Variable1 As Double
Public Variable2 As Double
Public Variable3 As Double
Public Sub New(Variable1 As Double, Variable2 As Double, Variable3 As Double)
Me.Variable1 = Variable1
Me.Variable2 = Variable2
Me.Variable3 = Variable3
End Sub
Public Sub Report()
Console.WriteLine()
Console.WriteLine("Variable1: {0},Variable2: {1},Variable3: {2}", Variable1, Variable2, Variable3)
End Sub
End Class
I also have another helper function which replaces the CPU intensive work that my real application would have, with a random delay between 0 and 1 second :
Public Async Function RandomDelayAsync() As Task
Await Task.Delay(TimeSpan.FromSeconds(Rnd()))
End Function
For demonstration purposes I have 2 version of my "work" function; an Async an non-Async version. Each of these functions takes and instance of DebugClass as a parameter, pretends to do some work on it and then simply returns the same object that it got as an input. :
'Async version
Public Async Function DoSomeWorkAsync(WorkObject As DebugClass) As Task(Of DebugClass)
Await RandomDelayAsync()
Return WorkObject
End Function
'Synchronous version
Public Function DoSomeWork(WorkObject As DebugClass) As DebugClass
RandomDelayAsync.Wait()
Return WorkObject
End Function
Lastly, i have a WaiterLoop, this function Awaits the created tasks for one to complete, prints the returned object's fields to the console and removes it from the list of tasks. It then waits for the next one to complete. In my real application i would do some more calculations here after i get the results from the individuals tasks, to see which parameters give the best results.
Public Async Function WaiterLoop(TaskList As List(Of Task(Of DebugClass))) As Task
Dim Completed As Task(Of DebugClass)
Do Until TaskList.Count = 0
Completed = Await Task.WhenAny(TaskList)
Completed.Result.Report()
TaskList.Remove(Completed)
Loop
End Function
Now, first consider this version of my Main() function:
Sub Main()
Randomize()
Dim Tasklist As New List(Of Task(Of DebugClass))
Dim anInstance As DebugClass
For var1 As Double = 0 To 5 Step 0.5
For var2 As Double = 1 To 10 Step 1
For Var3 As Double = -5 To 0 Step 1
anInstance = New DebugClass(var1, var2, Var3)
'adding an Async task to the tasklist
Tasklist.Add(DoSomeWorkAsync(anInstance))
Next
Next
Next
WaiterLoop(Tasklist).Wait()
Console.ReadLine()
End Sub
The output here is exactly as I would expect, the tasks all complete and for each of the parameter combinations made a line is printed to the console. All's good so far, the problem i'm facing arrises when this line:
Tasklist.Add(DoSomeWorkAsync(anInstance))
Is replaced with this line
Tasklist.Add(Task.Run(Function() DoSomeWork(anInstance)))
In this new version i don't call the Async version of the work function, instead i'm using Task.Run To run a normally synchronous function on a worker thread. This is where the s**t hits the fan.
Suddenly, the output is not as expected anymore;
'This is the type of output i now get:
Variable1: 1.5,Variable2: 7,Variable3: -1
Variable1: 5,Variable2: 10,Variable3: 0
Variable1: 5,Variable2: 10,Variable3: 0
Variable1: 5,Variable2: 10,Variable3: 0
Variable1: 5,Variable2: 10,Variable3: 0
Variable1: 5,Variable2: 10,Variable3: 0
Somehow all of the tasks i created now seem to be referring to the same instance of DebugClass, as everytime a tasks completes, the same output is printed. I don't understand why this happens, because i'm creating a new instance of DebugClass before each time i start a new task: anInstance = New DebugClass(var1, var2, Var3) followed by Tasklist.Add(Task.Run(Function() DoSomeWork(anInstance))). As soon as i assign a new instance of DebugClass to AnInstance, it "forget's" the previous instance it was storing, right?. And the instance referenced by each of the created tasks ought to be independent of the ones referred to by the other tasks?
Clearly, I am mistaken, but i would appreciate it if someone could explain to me what's going on here.
I also wonder why one is faster than the other, but i will save that for a separate question unless it's related to this issue.
Thank you for taking the time to read this.
Lambdas (ie Function() DoSomeWork(anInstance)) 'close'* on a reference to a variable NOT on its current value.
Thus Function() DoSomeWork(anInstance) means 'when you come to run perform the DoSomeWork method on the current value of anInstance'.
You only have one instance of anInstance because you declared it outside the loop.
Quick fix: Move the Dim anInstance As DebugClass statement inside the inner loop, this gives you one variable instance per loop, which is what you want.
See also Captured variable in a loop in C#, which is this basically the same question in c# and has some useful discussion/links in the comments
*Closures are a big topic, I'd suggest reading https://en.wikipedia.org/wiki/Closure_(computer_programming). Happy to discuss further in comments.

Is calling a subroutine inside Parallel.For (and passing a variable ByRef to it) thread safe?

I call a subroutine MyPartsMatrix inside nested Parallel.For loops (vb.net). MyPartsMatrix requires a variable called "unfilled" that is passed ByRef because this value is modified inside MyPartsMatrix subroutine. I need to grab and store this value after the subroutine MyPartsMatrix executes.
The "unfilled" variable yields a different value when I run the parallel version of this code compared to a one that is non-parallel, using normal nested For...Next loops. I can't figure out why this is the case.
Is it thread safe to call another subroutine from inside the Parallel.For loop?
Is this variable "unfilled" thread safe?
Dim ConcurrentListofResults As ConcurrentQueue(Of FindBestResults)
ConcurrentListofResults = New ConcurrentQueue(Of FindBestResults)
Dim x = 5, y = 5
Parallel.For(0, x, Sub(oD)
Parallel.For(0, y, Sub(oT)
Dim unfilled As Integer = 0
MyPartsMatrix (oD, oT, unfilled)
'Create a FBS item to add to the concurrent list collection
Dim FBSResultsItem = New FindBestResults
FBSResultsItem.oD = oD
FBSResultsItem.oT = oT
FBSResultsItem.unfilled = unfilled
'Add this item to the Concurent collection
ConcurrentListofResults.Enqueue(FBSResultsItem)
End Sub)
End Sub)
'Get best result.
Dim bestResult As FindBestResults
For Each item As FindBestResults In ConcurrentListofResults
If item.unfilled < bestResult.unfilled Then
bestResult.oD = item.oD
bestResult.oT = item.oT
bestResult.unfilled = item.unfilled
End If
Next
Public Sub MyPartsMatrix (ByVal oD As Integer, ByVal oT As Integer, ByRef unfilled As Integer)
'....do stuff with the unfilled variable....
'unfilled is a counter that is incremented while we run through the PartsMatrix
unfilled = unfilled + 1
End Sub
If this is not thread safe, is there another way to write this so that the "unfilled" variable is thread safe or to make calling another subroutine thread safe?
Without the definition of MakeSchedule (which you've called MyMakePartsMatrix in another place) it's impossible to say whether it's thread safe.
To be threadsafe the sub needs to not modify anything other than unfilled. This will ensure that calling it multiple times with the same inputs will always yield the same outputs. I'd also recommend converting to a function as I find this much easier to understand whats happening.
On another note:
Your performance will be better if you don't nest parallel loops. You're currently waiting for your inner loop to finish before launching your second loop. If you're using larger x + y values then something similar to the code will work better.
Dim scenarios = From x2 In Enumerable.Range(0, x)
From y2 In Enumerable.Range(0, y)
Select New With {x2, y2}
Parallel.ForEach(scenarios, Sub(s)
End Sub)

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.

Assigning Function Value Instead of Using Return Keyword, and its Effect on Synchronised Code Execution

I am wondering if there is a difference between using
Public Function Foo() As Double
Return 3.0
End Function
and
Public Function Foo() As Double
Foo = 3.0
End Function
but specifically with respect to code execution.
I am attempting to manage a multithreaded application using synchronisation, and am not sure if I am capturing every lock and release correctly.
I understand that code lines after 'Return' are not executed because the function loses focus, but what if the 'Return' is wrapped in a SyncLock block?
Public Function Foo() As Double
SyncLock fooLock
Return 3.0
End SyncLock
End Function
Does the End SyncLock get called? Is the SyncLock block shorthand for:
Public Function Foo() As Double
Dim result as Double
Try
Threading.Monitor.Enter(fooLock)
result = 3.0
Finally
Threading.Monitor.Exit(fooLock)
End Try
Return result
End Function
If my understanding is correct then the Finally block comes before the function releases focus, but alternatively if the Finally waits on the Return and subsequent code, then it may be a while before the Finally gets a chance, i.e.
Public Sub DoSomething()
Dim a As Double = Foo
...Do other things
End Sub
Public Function Foo() As Double
Try
Threading.Monitor.Enter(fooLock)
Return 3.0
...code returned to executes, 'a' is assigned to the return value of Foo, then perhaps all of the other tasks on the thread are done, then
Finally
Threading.Monitor.Exit(fooLock)
End Try
End Function
In this case my lock may have held for too long. For value types the first code would be acceptible, but for reference types, the first would release the lock then return a reference to the object and the consumer would have non-synced access to the value, the second may or may not work, depending on how much code is executed inbetween the break in the function.
Could anyone help me straighten these concepts out?
There is definitely a difference between RETURN 3 and v = 3
Return X terminates the call right there, but it definitely runs through any try catch finally's you might have open.
v = 3 simply sets up the return value as 3, but does not return. Execution continues on in the function until the end of function or an exit function.
I'm not 100% sure about the synclock question, but I'd wager than RETURNing out of it would terminate it properly.
Please don't use the "assign function name a value and return" pattern anymore. Its up there with REM for some of us.
That said, if you look at the IL generated from code that uses the return vs assign function name you'll see that it is 100% the same.
As for your other question, according to MSDN:
SyncLock block guarantees release of the lock, no matter how you exit the block

How do you find the last loop in a For Each (VB.NET)?

How can I determine if I'm in the final loop of a For Each statement in VB.NET?
The generally, collections on which you can perform For Each on implement the IEnumerator interface. This interface has only two methods, MoveNext and Reset and one property, Current.
Basically, when you use a For Each on a collection, it calls the MoveNext function and reads the value returned. If the value returned is True, it means there is a valid element in the collection and element is returned via the Current property. If there are no more elements in the collection, the MoveNext function returns False and the iteration is exited.
From the above explanation, it is clear that the For Each does not track the current position in the collection and so the answer to your question is a short No.
If, however, you still desire to know if you're on the last element in your collection, you can try the following code. It checks (using LINQ) if the current item is the last item.
For Each item in Collection
If item Is Collection.Last Then
'do something with your last item'
End If
Next
It is important to know that calling Last() on a collection will enumerate the entire collection. It is therefore not recommended to call Last() on the following types of collections:
Streaming collections
Computationally expensive collections
Collections with high tendency for mutation
For such collections, it is better to get an enumerator for the collection (via the GetEnumerator() function) so you can keep track of the items yourself. Below is a sample implementation via an extension method that yields the index of the item, as well as whether the current item is the first or last item in the collection.
<Extension()>
Public Iterator Function EnumerateEx(Of T)(collection As IEnumerable(Of T))
As IEnumerable(Of (value As T, index As Integer, isFirst As Boolean, isLast As Boolean))
Using e = collection.GetEnumerator()
Dim index = -1
Dim toYield As T
If e.MoveNext() Then
index += 1
toYield = e.Current
End If
While e.MoveNext()
Yield (toYield, index, index = 0, False)
index += 1
toYield = e.Current
End While
Yield (toYield, index, index = 0, True)
End Using
End Function
Here is a sample usage:
Sub Main()
Console.WriteLine("Index Value IsFirst IsLast")
Console.WriteLine("----- ----- ------- ------")
Dim fibonacci = {0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89}
For Each i In fibonacci.EnumerateEx()
Console.WriteLine(String.Join(" ", $"{i.index,5}",
$"{i.value,5}",
$"{i.isFirst,-7}",
$"{i.isLast,-6}"))
Next
Console.ReadLine()
End Sub
Output
Index Value IsFirst IsLast
----- ----- ------- ------
0 0 True False
1 1 False False
2 1 False False
3 2 False False
4 3 False False
5 5 False False
6 8 False False
7 13 False False
8 21 False False
9 34 False False
10 55 False False
11 89 False True
It probably would be easier to just use a For loop instead of ForEach. But, similarly, you could keep a counter inside your ForEach loop and see if its equal to yourCollection.Count - 1, then you are in the last iteration.
With a foreach, you cannot know this until it is too late (ie. you're out of the loop).
Note, I'm assuming you're using something where you only have an IEnumerable interface. If you have a list, array, etc. then follow the other answers here which uses .Count or similar to find out how many items there are, and thus you can keep track of where you are in the collection.
However, with just IEnumerable/IEnumerator, there is no way to know for sure wether or not there are more, if you use foreach.
If you need to know this, use IEnumerable yourself, which is what foreach does.
The below solution is for C# but should translate easily to VB.NET:
List<Int32> nums = new List<Int32>();
nums.Add(1);
nums.Add(2);
nums.Add(3);
IEnumerator<Int32> enumerator = nums.GetEnumerator();
if (enumerator.MoveNext())
{
// at least one value available
while (true)
{
// make a copy of it
Int32 current = enumerator.Current;
// determine if it was the last value
// if not, enumerator.Current is now the next value
if (enumerator.MoveNext())
{
Console.Out.WriteLine("not last: " + current);
}
else
{
Console.Out.WriteLine("last: " + current);
break;
}
}
}
enumerator.Dispose();
This will print:
not last: 1
not last: 2
last: 3
The trick is to take a copy of the current value, then ask the enumerator to attempt to move on to the next one. If that fails, the copy you made was indeed the last value, otherwise there is more.
here is a simple thing i dont know if it is politically correct but it works
for each item in listOfThings
if(item = listOfThings.last)then
'you found the last loop
end if
next
Short answer: You can't
Long answer: There's nothing in the semantics of a For Each statement that allows you to identify whether you're running the first, last or any particular iteration.
For Each is built in the IEnumerable and IEnumerable<> interfaces, and the behavior is dependent on the implementation you're calling. It's valid, though confusing, for the collection you're iterating to return elements in a different order every time. Fortunately, List<> doesn't do this.
If you really need to know that a particular iteration is the last, you could identify the last element (in advance) and then take different action when you encounter that element.
Easier would be to detect the first iteration (say, through a boolean) and then do something different.
An example (C#, but the VB will be similar):
StringBuilder result = new StringBuilder();
bool firstTime = true;
foreach(string s in names)
{
if (!firstTime)
{
result.Append(", ");
}
result.Append(s);
firstTime = false;
}
Check if the element is the last element of the container.
Why do you want to do that?
You could just place instructions after the loop. (That you execute on the last element)
It would be easier to use a For loop instead of a ForEach, however you could do something like
If item.Equals(itemCollection(itemCollection.Count)) Then
...
End If
inside of your ForEach loop... Assuming the object has properly overridden the Equals method.
This is probably much more resource intensive than just using a For loop or keeping track of the current index in a separate variable.
I'm not sure if this is the correct syntax, it's been a long time since I've used VB.
Using a standard "For Each" loop you can't. The intent of a "for Each" loop is to allow you to concentrate on the data in lieu of the underlying collection.
I keep coming back to this post, so I decided to sit down and think about this issue and I came up with this:
For Each obj In myCollection
Console.WriteLine("current object: {0}", obj.ToString)
If Object.ReferenceEquals(obj, myCollection.Last()) Then
Console.WriteLine("current object is the last object")
End If
Next
You could even reduce the If...Then...End If statement to a single line if you wanted to. I'm not sure if calling .Last() on every iteration has a large impact on performance, but you can always assign it to a variable outside of the loop if that's what keeps you awake at night.
this code sample might help
For Each item in Collection
If ReferenceEquals(Collection.Item(Collection.Count - 1), item) Then
'do something with your last item'
End If
Next
If you are tied to IEnumerable. Do you have to be inside the foreach loop? If not you could declare a variable just before the foreach loop. Set it during the loop. Then, use it after the loop (if its not null)
Well there are few workaround
Suppose you are working with CheckBoxList
Then this code sample might help :
Dim count As Integer = 0
Dim totalListCount As Integer = CheckBoxList.Items.Count()
For Each li In CheckBoxList.Items
count += 1
// This is the Last Item
If count = totalListCount THEN
// DO Somthing.....
END IF
NEXT
For Each xObj In xColl
If xObj = xColl(xColl.Count) Then ...
Next