Looping Through List Inside Parallel.Foreach - vb.net

I would like to iterate through a list inside of a Parallel.ForEach loop, but the list will be a shared resource that I have to take turns accessing, which I think would defeat the purpose of the parallelism. Access to the list is read-only, so I was wondering if there was a way to make copies of the list, and allow each thread to pick one if it is not in use by one of the other threads.
Dim collCopy1 As List(Of InnerType) = innerCollection.ToList()
Dim collCopy2 As List(Of InnerType) = innerCollection.ToList()
Dim collCopy3 As List(Of InnerType) = innerCollection.ToList()
Dim collCopy4 As List(Of InnerType) = innerCollection.ToList()
Dim Lock As New Object
Dim ParallelOpts As New ParallelOptions()
ParallelOpts.MaxDegreeOfParallelism = 4
Task.Factory.StartNew(Sub()
Parallel.ForEach(outerCollection,
ParallelOpts,
Sub(outerItem As OuterType)
'Pick from collCopy1, collCopy2, collCopy3, collCopy4 here, assign to innerList, and SyncLock it
For Each innerItem As InnerType In innerList
'Do some stuff
Next
End Sub)
End Sub)

Related

Using TextBox as Path Like OpenFileDialog

I hope someone can help me.
In short, I have this little code
I can't replace "Path from my pc" with a string/textbox in any way
The intention would be this:
enumerator = SpotifyBox.Text.Split.GetEnumerator
It doesn't give me errors, but it doesn't work as it should once the button starts
Dim enumerator As List(Of String).Enumerator = New List(Of String).Enumerator()
Dim class70 As Action(Of String())
ThreadPool.SetMinThreads(40, 40)
Dim strArrays As List(Of String()) = New List(Of String())()
Try
Try
enumerator = File.ReadLines(Path from pc).ToList().GetEnumerator()
While enumerator.MoveNext()
Dim current As String = enumerator.Current
If (If(Not current.Contains(":"), True, String.IsNullOrEmpty(current))) Then
Continue While
End If
strArrays.Add(current.Split(New Char() {":"c}))
End While
Finally
DirectCast(enumerator, IDisposable).Dispose()
End Try
int_5 = strArrays.Count
Catch exception1 As System.Exception
End Try
That is some crazy code and the question you're asking is not the question you need an answer to. Based on what you have posted, it seems that you have a file path in a TextBox named SpotifyBox and you want to read the lines from that file with some processing. In that case, get rid of all that craziness and do this:
Dim filePath = SpotifyBox.Text
Dim records As New List(Of String())
For Each line In File.ReadLines(filePath)
If line.Contains(":") Then
records.Add(line.Split(":"c))
End If
Next
That's it, that's all. You pretty much never need to create an enumerator directly. Just use a For Each loop.

Is there a method to add to a list (of list(of String)) byVal?

I'd like to repeatedly add lists of strings to a bigger list collection, but it seems that the list.add function is adding the items byref. How can I change the below example code to pass the elements byval so that they're not cleared when I re-use variables:
Dim inner As New List(Of String)
Dim outer As New List(Of List(Of String))
Dim eleCount As Integer = 0
lbltop:
inner.Add("a")
inner.Add("b")
inner.Add("c")
outer.Add(inner)
Debug.Write(outer(0)(0).ToString())
inner.Clear()
Debug.Write(outer(0)(0).ToString())
eleCount += 1
If eleCount < 2 Then
GoTo lbltop
End If
this writes a then there's an out of range exception for the next debug.write statement.
I'd like it to write aa then loop to add another inner element.
List<T> is a reference type. Only way to not effect it when calling Clear() is to add a clone of it to outer instead. You can do so by using .ToList()
outer.Add(inner.ToList())
But I actually think the most clear way is to instead assign inner a new List instead of calling Clear, than you dont have to do above.
inner = new List(Of String)

VB.Net Use XmlNodeList in a parallel ForEach

I have a piece of code that iterates over the nodes in an XmlNodeList and creates a different object for each one depending on the node name and adds it to a list for printing.
For Each node as XmlNode In nodeList
Select Case node.Name.ToUpper()
Case "SHAPE"
_items.Add(New ShapeTemplate(node, Me))
Case "TEXTBLOCK"
_items.Add(New TextblockTemplate(node, Me))
End Select
Next
This code works fine, but because of all the work that has to be done by the ShapeTemplate and TextblockTemplate constructors, it is VERY slow. Since the order objects are added to _items doesn't matter, I thought a good way to speed it up would be to use a parallel.ForEach loop. The problem is XmlNodeList can't be used with parallel.ForEach because it is a non-generic collection. I've been looking into ways to convert XmlNodeList to List(Of XmlNode) with no luck. The answer I keep seeing come up is
Dim nodes as New List(Of xmlNode)(nodeList.Cast(Of xmlNode)())
But when I try it, I get an error telling me that 'Cast' is not a member of XmlNodeList.
I've also tried using TryCast like this
Dim nodes as List(Of XmlNode) = TryCast(CObj(nodeList), List(Of XmlNode))
but it results in nodes being Nothing because the object can't be cast.
Does anyone know how I can use XmlNodeList in a parallel.ForEach loop?
EDIT: I'm trying to avoid using a loop for the conversion if I can
You could use Parallel LINQ instead of Parallel.ForEach, which seems like a more natural fit for this sort of transformation. This looks just like a normal LINQ query, but with .AsParallel() added.
Imports System.Linq
' _items will not be in same order as nodes in nodeList.
' Add .AsOrdered() after .AsParallel() to maintain order, if needed.
_items = (
From node In nodeList.Cast(Of XmlNode)().AsParallel()
Select item = CreateTemplate(node)
Where item IsNot Nothing
).ToList()
Function CreateTemplate(node As XmlNode) As ITemplate ' interface or base class for your types
Dim item As ITemplate
Select Case node.Name.ToUpper()
Case "SHAPE"
item = New ShapeTemplate(node, Me)
Case "TEXTBLOCK"
item = New TextblockTemplate(node, Me)
Case Else
item = Nothing
End Select
Return item
End Function
As seen here, the XmlNodeList can be converted to a generic List(Of XmlNode) by passing it to the constructor with OfType.
' I added so your code could compile.
' I assumed an interface shared by ShapeTemplate and TextblockTemplate
Dim nodeList As XmlNodeList
Dim _items As New List(Of ITemplate)
Dim _nodes As New List(Of XmlNode)(nodeList.OfType(Of XmlNode))
Now the parallel loop. Note, if you are adding to a non-threadsafe collection such as List, you will need to synchronize adding the objects to the list. So i separated the time consuming portion (Template constructor) from the fast operation (adding to the list). This should improve your performance.
Dim oLock As New Object
Parallel.ForEach(
_nodes,
Sub(node)
Dim item As ITemplate
Select Case node.Name.ToUpper()
Case "SHAPE"
item = New ShapeTemplate(node, Me)
Case "TEXTBLOCK"
item = New TextblockTemplate(node, Me)
Case Else
item = Nothing ' or, do something else?
End Select
SyncLock oLock
_items.Add(item)
End SyncLock
End Sub)

VB: List(Of List(Of String)) keeps changing the content of the outer list when I change the inner one?

I'm writing a program in VB, and I need to make a list of lists (I've already figured out how to do that one). The problem is, the outer list is going to need a different number of elements depending on other variables elsewhere in the program.
I've looped this code:
Dim rep As Long = 1023
Dim items As List(Of String)
items.Add("First Entry")
items.Add("Second Entry")
items.Add("Third Entry")
items.Add("Fourth Entry")
'(sake of argument, these are the variables
'that will be changing vastly earlier
'in the program, I put them in this way to simplify
'this part of my code and still have it work)
Dim myList As New List(Of List(Of String))
Dim tempList As New List(Of String)
For index = 1 To Len(rep.ToString)
tempList.Add(items(CInt(Mid(rep.ToString, index, 1))))
Next
myList.Add(tempList)
tempList.Clear()
My issue is with that last part; every time I add the tempList to myList, it's fine, but when I clear tempList, it also clears the version of tempList in myList.
myList will have a count of 1, but the list inside it has a count of 0 as soon as I clear tempList. And I have to clear tempList because I'm looping this section of code over and over, a variable number of times.
Is there a way around this? Am I being a horrible noob?
You're using the same tempList each time, instead of making a new one.
You likely need to do:
myList.Add(tempList)
tempList = new List(Of String) ' Create a new List(Of T), don't reuse...

Generic Lists copying references rather than creating a copiedList

I was developing a small function when trying to run an enumerator across a list and then carry out some action. (Below is an idea of what I was trying to do.
When trying to remove I got a "Collection cannot be modified" which after I had actually woken up I realised that tempList must have just been assigned myLists reference rather than a copy of myLists. After that I tried to find a way to say
tempList = myList.copy
However nothing seems to exist?? I ended up writing a small for loop that then just added each item from myLsit into tempList but I would have thought there would have been another mechanism (like clone??)
So my question(s):
is my assumption about tempList receiving a reference to myList correct
How should a list be copied to another list?
private myList as List (Of something)
sub new()
myList.add(new Something)
end sub
sub myCalledFunction()
dim tempList as new List (Of Something)
tempList = myList
Using i as IEnumerator = myList.getEnumarator
while i.moveNext
'if some critria is met then
tempList.remove(i.current)
end
end using
end sub
By writing tempList = myList you don't make a copy oh the collection, you only make tempList reference myList. Try this instead : dim tempList as new List (Of Something)(myList)
I think if you called myCalledFunction(byVal aListCopy as Something) you can let the framework do the work.
If your list consists of value types you can just create a new list with the old list passed in the constructor. If you are going to be doing a deep copy of a reference object your best bet is to have your reference type implement ICloneable (example). You can then loop through and clone each object or you could add an extension method (like this c# example).
Try this - use LINQ to create a new list from the original, for example:
Sub Main()
Dim nums As New List(Of Integer)
nums.Add(1)
nums.Add(2)
nums.Add(3)
nums.Add(4)
Dim k = (From i In nums _
Select i).ToList()
For Each number As Integer In nums
k.Remove(number)
Next
End Sub
k will then be a new list of numbers which are not linked to the source.