Trying to add a node to a graph - c#

I am implementing a Graph via adjacency list, and I am trying to add a node to a Graph class but I keep getting an error
System.NullReferenceException has been thrown
this is the bit of my code:
MyNode class
class node
{
public List<object>? Neighbors { get; set; }
public int Data { get; set; }
public node() { }
public node(int val)
{
Data = val;
}
}
This is my graph class:
class Graph
{
// Attributes of the Class
public int NumVertices { get; set; } // Number of vertices
public List<node> vertices { get; set; } // list of nodes in the graph
// Graph constructor
public Graph(int size)
{
NumVertices = size;
vertices = new List<node>();
//// Allocate node objects at each index of the vertice list
//for (int i = 0; i < NumVertices; i++)
//{
// Instantiate(vertices[i]);
//}
}
//Adding ege to vertice
public void addEdge(node a, node b)
{
a.Neighbors.Add(b); //---> it referenced here
b.Neighbors.Add(a);
}
Then testing functions: this Instantiates node objects and
public static void TestGraph()
{
node a = new node(1);
node b = new node(2);
node c = new node(3);
node d = new node(4);
node e = new node(5);
node f = new node(6);
Graph myGraph = new Graph(6) ;
//// Add vertices to the node
//myGraph.vertices.Add(a);
//myGraph.vertices.Add(b);
//myGraph.vertices.Add(c);
//myGraph.vertices.Add(d);
//myGraph.vertices.Add(e);
////myGraph.vertices.Add(f);
myGraph.addEdge(a, b); //---> Error originates here
myGraph.addEdge(a, c);
myGraph.addEdge(a, d);
myGraph.addEdge(b, c);
myGraph.addEdge(c, e);
myGraph.addEdge(d, e);
Console.WriteLine("Cheching if a and b are neighbors " + myGraph.isAdjacent(a, b).ToString());
Console.WriteLine("Cheching if a and c are neighbors " + myGraph.isAdjacent(a, c).ToString());
How would I be able to rectify this issues? Thanks

There is no problem with the node object but it's Neighbors field which is supposed to be a List but the constructor doesn't have any initialization for it.
If you change your constructors to initialize Neighbors, the null reference error will go away. Below is an example to how you can initialize the list.
public node(int val)
{
Data = val;
Neighbors = new List<object>();
}

You have never created a Neighbors list, so it will always be null.
I would formulate those lists as getter-only properties with an initializer. Then the lists get initialized automatically and you can't forget to do it later.
class Node
{
public List<Node> Neighbors { get; } = new();
public int Data { get; set; }
public Node() { }
public Node(int val)
{
Data = val;
}
}
Also, this must be a List<Node>, not a List<object>, since you will always add nodes to it. Alternatively, the Neighbors list could also be a List<int> storing the indices of the neighbors in the Graph.Vertices list instead of the nodes themselves.
Graph has a property public int NumVertices { get; set; }. Isn't NumVertices equivalent to Vertices.Count? Since you pass the graph size to the constructor, it makes sense to add this number of nodes the Vertices list in the constructor. NumVertices then automatically matches this size since it returns Vertices.Count.
class Graph
{
public int NumVertices => Vertices.Count;
public List<Node> Vertices { get; } = new();
public Graph(int size)
{
for (int i = 0; i < size; i++) {
Vertices.Add(new Node());
}
}
public void AddEdge(int i, int j)
{
Node a = Vertices[i];
Node b = Vertices[j];
a.Neighbors.Add(b);
b.Neighbors.Add(a);
}
public bool AreAdjacent(int i, int j)
{
return Vertices[i].Neighbors.Contains(Vertices[j]);
}
}
Since the nodes are stored in the Graph object, it makes sense to pass their index in the AddEdge and AreAdjacent methods instead of keeping another reference of them outsize of Graph object. This is better for consistency, as it is only possible to add edges between nodes that are actually in this graph.
Adding edges:
var graph = new Graph(6);
graph.AddEdge(0, 1);
graph.AddEdge(0, 2);
graph.AddEdge(0, 3);
graph.AddEdge(1, 2);
graph.AddEdge(2, 4);
graph.AddEdge(3, 4);

myGraph.addEdge(a, b); //---> Error originates here
So, what is the value of a? Your posted code leaves it uninitialized.

Related

How to check property value of object

i want do some action if value of property in object is equal something.
My object:
//abstract class Tile.cs
namespace DungeonGame.Tiles
{
public abstract class Tile
{
public abstract string Type { get; set; }
}
}
//Item.cs that iherits from Tile.cs
namespace DungeonGame
{
public class Item : Tile
{
public override string Type { get; set; }
public string Name { get; set; }
public Item(string name)
{
this.Type = "item";
this.Name = name;
}
}
}
And i have several objects in public Object[,] BoardTiles { get; private set; }, I had to store different types of tiles in same place so i had to use Object type array.
What i'm trying to do is replace array index with different type of object, depending of the value of property (In this case I set value depending of object type not his value of property):
public void movefillRight(int playerPositionRowIndex, int playerPositionColumnIndex)
{
for (int r = 0; r < Rows; r++)
{
for (int c = 0; c < Cols; c++)
{
if (BoardTiles[playerPositionRowIndex, playerPositionColumnIndex ].GetType().ToString() == ("DungeonGame.Item"))
{
/* placePlayerOnTheBoard(playerPositionRowIndex, playerPositionColumnIndex, PlayerTile);
*/ BoardTiles[playerPositionRowIndex, playerPositionColumnIndex] = Item1;
BoardTiles[playerPositionRowIndex, playerPositionColumnIndex - 1] = Floor;
}
else if (BoardTiles[playerPositionRowIndex, playerPositionColumnIndex-1].GetType().ToString() == ("DungeonGame.Item"))
{
BoardTiles[playerPositionRowIndex, playerPositionColumnIndex-1] = Item1;
placePlayerOnTheBoard(playerPositionRowIndex, playerPositionColumnIndex, PlayerTile);
}
else
{
placePlayerOnTheBoard(playerPositionRowIndex, playerPositionColumnIndex, PlayerTile);
BoardTiles[playerPositionRowIndex, playerPositionColumnIndex - 1] = Floor;
}
}
}
Console.WriteLine(BoardTiles[playerPositionRowIndex, playerPositionColumnIndex].GetType().ToString());//log
}
The functionality of the function is to leave the same object on the same index when the player walks over it, so the problem is that code is ok when I have one Item, for example only sword, but when there are more items i can replace index always with same object but not with object witch different properties, when the player walks over a good sword and bad sword (same object, but another properties) he leave behind just sword... I have idea how to solve this problem but i need to refer to property value which is at specific index of my object array.
After you check that the item in the array is of a specific type you can then cast it.
if (BoardTiles[playerPositionRowIndex, playerPositionColumnIndex ].GetType().ToString() == ("DungeonGame.Item"))
{
var item = (DungeonGame.Item)BoardTiles[playerPositionRowIndex, playerPositionColumnIndex];
Console.Writeline(item.Name);
}

How to parse list of data dynamically and show the result in treeview

I have a list of objects with properties. The object class shown below:
public class ElementImpression
{
public int ElementId { get; private set; }
public string FamilyAndTypeName { get; private set; }
public string CategoryName { get; private set; }
public int CategoryNumber { get; private set; }
public string SystemAbbreviation { get; private set; }
public ElementImpression(Element e)
{
ElementId = e.Id.IntegerValue;
FamilyAndTypeName = e.get_Parameter(BuiltInParameter.ELEM_FAMILY_AND_TYPE_PARAM).AsValueString();
CategoryName = e.Category.Name;
CategoryNumber = e.Category.Id.IntegerValue;
SystemAbbreviation = e.get_Parameter(BuiltInParameter.RBS_DUCT_PIPE_SYSTEM_ABBREVIATION_PARAM).AsString();
}
}
The goal is to parse the list and create a structured, hierarchical presentation in a TreeView control. The number of levels in the hierarchy and which properties to use as nodes is defined at runtime by the user.
I have been succesful in creating the following treeview:
Treeview by using the following code:
private void UpdateTreeView(object sender, MyEventArgs e)
{
//Level 0: All
//Level 1: System Abbreviation
//Level 2: Category Name
//Level 3: Family and Type Name
treeView1.BeginUpdate();
treeView1.Nodes.Clear();
//Payload is a container object holding the list to be parsed. It is cached as a property in the form.
//Payload.ElementsInSelection is the list of objects to parse.
var lv1Group = Payload.ElementsInSelection.GroupBy(x => x.SystemAbbreviation);
treeView1.Nodes.Add("All");
int i = -1;
foreach (IGrouping<string, ElementImpression> group1 in lv1Group)
{
treeView1.Nodes[0].Nodes.Add(group1.Key);
var lv2Group = group1.ToList().GroupBy(x => x.CategoryName);
i++;
int j = -1;
foreach (IGrouping<string,ElementImpression> group2 in lv2Group)
{
treeView1.Nodes[0].Nodes[i].Nodes.Add(group2.Key);
var lv3Group = group2.ToList();
j++;
int k = -1;
foreach (ElementImpression ei in lv3Group)
{
k++;
treeView1.Nodes[0].Nodes[i].Nodes[j].Nodes.Add(ei.FamilyAndTypeName);
treeView1.Nodes[0].Nodes[i].Nodes[j].Nodes[k].Nodes.Add(ei.ElementId.ToString());
treeView1.Nodes[0].Nodes[i].Nodes[j].Nodes[k].Nodes.Add(ei.CategoryNumber.ToString());
}
}
}
treeView1.EndUpdate();
}
Is it possible to rewrite the UpdateTreeView() method, so that it accepts some kind of object, which tells the method how many levels and what properties to use, and then parses the data and creates the treeview dynamically at runtime? Is it possible to do it using recursion?
Well, I managed to arrive at a solution. It is not recursive, but it is dynamic. Properties can be added or removed at runtime and the following code should parse it and populate the tree.
The general idea is to loop through all the objects which need to be organized to determine the FullPath of the deepest level node. Because I am using property values as nodes, each object already holds its' FullPath subparts. They just need to be combined in the correct order.
Then, starting from the root node at the top level, which is added manually, walk every step of the path checking if the node exists and if not creating it. This method successfully renders my data as needed in a treeview.
The FindTreeNodeByFullPath() method is from here.
public void PopulateTreeview()
{
//Manually add root node
treeView1.Nodes.Add("All");
//Loop all the objects
foreach (ElementImpression e in Elements)
{
//Declare array to hold the names of all nodes in path to the element
//PropertiesList is an object containing information about what properties to consider and how many.
string[] pathParts = new string[PropertiesList.Length + 2];
//The name of root node
pathParts[0] = "All";
//Populate the path parts with values from elements
for (int i = 0; i < PropertiesList.Length; i++)
{
pathParts[i + 1] = PropertiesList[i].getPropertyValue(e);
}
//Finish the list with the name of the element (id currently)
pathParts[pathParts.Length - 1] = e.Id.IntegerValue.ToString();
//Create an array of all full paths from root node to the element
string[] fullPaths = new string[PropertiesList.Length + 2];
for (int i = 0; i < fullPaths.Length; i++)
{
if (i == 0) fullPaths[i] = pathParts[i];
else fullPaths[i] = fullPaths[i - 1] + "." + pathParts[i];
}
//Iterate through the fullPaths to determine, if node exists, if not -> create it
TreeNode previousNode = null;
for (int i = 0; i < fullPaths.Length; i++)
{
TreeNode foundNode = treeView1.Nodes.FindTreeNodeByFullPath(fullPaths[i]);
if (foundNode == null)
{
if (previousNode != null) previousNode = previousNode.Nodes.Add(pathParts[i]);
}
else
{
previousNode = foundNode;
continue;
}
}
}
}

Simple List Merge C# (Without Generics)

I have created a simple list class from scratch. This is for a class assignment that I have been working on for about a week - very new to lists. We can not use generics so trying to research my question below has not been fruitful. Although I did get to watch 7 tutorials on youtube by BetterCoder and I found some stuff in my book but nothing with an example of "merging".
I have three classes - my node, my list, and my program. In my list class, I am working on building a Merge() method which eventually will compare the data in the two lists and merge them into an ordered list.
Right now for some reason my Merge method - which is very basic to help me understand what is happening - is not working correctly. It has both lists passed to it, and is adding the data from listTwo to listOne BUT for some reason when it's printing to the console the second Node's Data shows twice :
EX: 1 -> 2 -> 2
instead of printing the head (1), the next (2) and then the next (3) which it should be.
EX: 1 -> 2 -> 3
In the program class I have proven with a write line that (listOne.firstNode.Next.Next.Data) = 3 . Which it should be.
Can someone help me figure out if the nodes in list one aren't pointing to each other correctly or whatever is going on?
My Merge Method must be passed both list objects (listOne and listTwo) and eventually I need to make those passed as references but I haven't figured that out quite yet and will focus on that later I suppose.
namespace LinkedList
{
//This is my Node Class
class Node
{
public object Data { get; set; }
public Node Next { get; set; }
public Node(object dataValue) : this(dataValue, null) { }
public Node(object dataValue, Node nextNode)
{
Data = dataValue;
Next = nextNode;
}
}
//This is my List Class
class List
{
public Node firstNode;
public int count;
public List()
{
firstNode = null;
}
public bool Empty
{
get { return this.count == 0; }
}
public int Count
{
get { return this.count; }
}
public object Add(int index, object o)
{
if (index < 0)
throw new ArgumentOutOfRangeException("Index: " + index);
if (index > count)
index = count;
Node current = this.firstNode;
if (this.Empty || index == 0)
{
this.firstNode = new Node(o, this.firstNode);
}
else
{
for (int i = 0; i < index - 1; i++)
current = current.Next;
current.Next = new Node(o, current.Next);
}
count++;
return o;
}
public object Add(object o)
{
return this.Add(count, o);
}
public object Merge(List a, List b)
{
a.Add(b.firstNode.Data);
return a;
}
public void Print()
{
while (this.count > 0)
{
Console.Write(firstNode.Data + "->");
if(firstNode.Next != null)
firstNode.Data = firstNode.Next.Data;
count--;
}
}
}
//And here is my Program
class Program
{
static void Main(string[] args)
{
List listOne = new List();
List listTwo = new List();
listOne.Add(1);
listOne.Add(2);
listTwo.Add(3);
listTwo.Print();
Console.WriteLine("");
listOne.Merge(listOne, listTwo);
Console.WriteLine("");
listOne.Print();
//This line below shows that the data "3" from listTwo is being added to listOne in the list Merge Method
//Console.WriteLine(listOne.firstNode.Next.Next.Data);
Console.ReadKey();
}
}
}
Actual problem in your print method
public void Print()
{
Node node = firstNode;
for (int i = 0; i < this.count; i++)
{
Console.Write(node.Data + "->");
if (node.Next != null)
node = node.Next;
}
}
Alex Sikilinda , you are right the merge method is incomplete.
public object Merge(List a, List b)
{
Node bNode = b.firstNode;
while (bNode != null)
{
a.Add(bNode.Data);
bNode = bNode.Next;
}
return a;
}
I would write it this way:
public void Merge(List b)
{
Node lastNode = GetLastNode();
if (lastNode != null)
{
lastNode.Next = b.firstNode;
}
else
{
this.firstNode = b.firstNode;
}
}
// this method is used to find the last node in current list
private Node GetLastNode()
{
if (this.firstNode == null)
{
return null;
}
Node current = this.firstNode;
while (current.Next != null)
{
current = current.Next;
}
return current;
}
First of all, I changed signature of Merge from public object Merge(List a, List b) to public void Merge(List b). Now we can use it like this:
listOne.Merge(listTwo);
This will link listOne's last element with the first element of listTwo and thus they are merged.
Now we need to change Print method since current version modifies the list, which shouldn't happen:
public void Print()
{
Node currentNode = this.firstNode;
while(currentNode != null)
{
Console.Write(currentNode.Data + ' ');
currentNode = currentNode.Next;
}
}
Instead of assigning the data back to first node I assign the
firstNode = firstNode.Next;
Please check the below Print Code
public void Print()
{
while (this.count > 0)
{
Console.Write(firstNode.Data + "->");
if (firstNode.Next != null)
firstNode = firstNode.Next;
count--;
}
}

Is there any way to access fields of 'super' class (not inherited)

Title may be misleading as I had some trouble searching and even creating a proper question, so let me give a real problem I'm struggling with:
I have a Graph class. Since graphs need nodes and edges I created two additional classes Node (vertex) and Edge. My structure looks like this:
class Graph
{
List<Node> nodes;
List<Edge> edges;
public Graph( ... ) { /* populate lists */ }
}
class Node { ... }
class Edge { ... }
I wrote some methods for Node class, one is particularly problematic for me. Signature:
public List<Node> GetNeighbours(List<Edge> edges) { ... }
Pretty standard. Given a graph I ask a node: how many neighbours do you have? I need list of edges to resolve it.
How can I refactor this code so that I can use Graph properties/fields inside instead of passing a list of edges every time? Is something like this possible:
public List<Node> GetNeighbours()
{
// ...
foreach(edge in *BASE*.edges) { ... }
}
I know that I can't use the base keyword because I don't want any inheritance here (why would a node have to inherit from graph?!) and nested classes seem not to help me as well (no access to "parent's" fields).
This code is working right now, but I feel it's not elegant and I'd like to experience a proper solution.
Pass a reference to the parent class in the Graph constructor.
Something like:
class Graph
{
private ParentType parent;
public void Graph(ref ParentType parent)
{
this.parent = parent;
}
}
Then, in the GetNeighbours method (assuming the ParentType has an Edges collection property):
public List<Node> GetNeighbours()
{
// ...
foreach(var edge in parent.Edges) { ... }
}
From this description of what you're trying to do:
Given a graph I ask a node: how many neighbours do you have?
Are you sure that this should be a method of a Node? Since Graph contains the Nodes and Edges perhaps this method is better off in Graph.
public List<Node> GetNeighbours(Node node)
{
if(!nodes.Contains(node)
{
return new List<Node>(); //No neighbors. Return an empty list.
}
// Find and return the neighbors. This method is in Graph so it
// has access to all of Graph's internals.
}
My reasoning is that since in a sense Graph is a parent and it contains Nodes, Node does not need to know about Graph. Its purpose (Single Responsibility) is complete without any references to Graph.
I would have a method like Graph.AddNodes() or Graph.AddEdges() on Graph so that this is a central place to make sure that all Nodes (and/or Edges) have the reference that it needs. I'm thinking something like this, depending on the model of Node and Edge for you.
class Graph
{
List<Node> nodes;
List<Edge> edges;
public Graph( ... ) { /* populate lists */ }
public void AddEdges(params Edge[] edges) {
foreach (var edge in edges) {
edge.Node1.Parent = this;
edge.Node2.Parent = this;
}
}
}
class Node {
public Graph Parent { get; set; }
public List<Node> GetNeighbours()
{
var neighbors = new List<Node>();
foreach(var edge in parent.Edges) {
if (edge.Node1 == this && !neighbors.Contains(edge.Node2)) {
neighbors.Add(edge.Node2);
}
else if (edge.Node2 == this && !neighbors.Contains(edge.Node1)) {
neighbors.Add(edge.Node1);
}
}
}
}
class Edge {
public Node Node1 { get; set; }
public Node Node2 { get; set; }
}
Here is an alternative approach. Instead of passing the parent reference, you could make each edge aware of the nodes on each end. And make each node aware of the edges connected to them.
A massive advantage of this is that you do not need enumerate possibly massive amounts of nodes/edges to find what you need. You already have what you need so it is much faster.
Here is quick sample of the approach I described along with some tests:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace GraphModelTest
{
class Program
{
static void Main(string[] args)
{
TestA();
TestB();
TestC();
}
private static void TestC()
{
//A <-> B
//| |
//D <-> C
Node a = new Node("a");
Node b = new Node("b");
Node c = new Node("c");
Node d = new Node("d");
Edge ab = a.ConnectTo(b);
Edge bc = b.ConnectTo(c);
Edge cd = c.ConnectTo(d);
Edge da = d.ConnectTo(a);
Graph g = new Graph();
g.Nodes.Add(a);
g.Nodes.Add(b);
g.Nodes.Add(c);
g.Nodes.Add(d);
g.Edges.Add(ab);
g.Edges.Add(bc);
g.Edges.Add(cd);
g.Edges.Add(da);
Console.WriteLine(g.ToString());
Console.WriteLine("Neighbours of B");
foreach (Node n in b.GetNeighbours())
{
Console.WriteLine(n.ToString());
}
Console.WriteLine("Neighbours of D");
foreach (Node n in d.GetNeighbours())
{
Console.WriteLine(n.ToString());
}
}
private static void TestB()
{
//A <-> B <-> C
Node a = new Node("a");
Node b = new Node("b");
Edge ab = a.ConnectTo(b);
Node c = new Node("c");
Edge bc = b.ConnectTo(c);
Graph g = new Graph();
g.Nodes.Add(a);
g.Nodes.Add(b);
g.Nodes.Add(c);
g.Edges.Add(ab);
g.Edges.Add(bc);
Console.WriteLine(g.ToString());
Console.WriteLine("Neighbours of B");
foreach (Node n in b.GetNeighbours())
{
Console.WriteLine(n.ToString());
}
}
private static void TestA()
{
//A <-> B
Node a = new Node("a");
Node b = new Node("b");
Edge ab = a.ConnectTo(b);
Graph g = new Graph();
g.Nodes.Add(a);
g.Nodes.Add(b);
g.Edges.Add(ab);
Console.WriteLine(g.ToString());
}
}
class Edge
{
public Edge(string name, Node a, Node b)
{
Name = name;
A = a;
B = b;
}
public Node A { get; private set; }
public Node B { get; private set; }
public string Name { get; private set; }
public override string ToString() => $"{Name}";
}
class Node
{
public Node(string name)
{
Name = name;
connectedEdges = new List<Edge>();
}
public string Name { get; private set; }
private ICollection<Edge> connectedEdges;
public IEnumerable<Edge> ConnectedEdges
{
get
{
return connectedEdges.AsEnumerable();
}
}
public void AddConnectedEdge(Edge e)
{
connectedEdges.Add(e);
}
public Edge ConnectTo(Node n)
{
//Create the edge with references to nodes
Edge e = new Edge($"{Name} <-> {n.Name}", this, n);
//Add edge reference to this node
AddConnectedEdge(e);
//Add edge reference to the other node
n.AddConnectedEdge(e);
return e;
}
public IEnumerable<Node> GetNeighbours()
{
foreach (Edge e in ConnectedEdges)
{
//Have to figure which one is not this node
Node node = e.A != this ? e.A : e.B;
yield return node;
}
}
public override string ToString() => $"{Name}";
}
class Graph
{
public Graph()
{
Nodes = new List<Node>();
Edges = new List<Edge>();
}
public ICollection<Node> Nodes { get; set; }
public ICollection<Edge> Edges { get; set; }
public override string ToString()
{
StringBuilder str = new StringBuilder();
str.AppendLine("Graph:");
str.AppendLine("Nodes:");
foreach (Node n in Nodes)
{
str.AppendLine(n.ToString());
}
str.AppendLine("Edges:");
foreach (Edge e in Edges)
{
str.AppendLine(e.ToString());
}
return str.ToString();
}
}
}

Bug in parallel tree printing method

Classes:
public class Tree
{
public Node RootNode { get; set; }
}
public class Node
{
public int Key { get; set; }
public object Value { get; set; }
public Node ParentNode { get; set; }
public List<Node> Nodes { get; set; }
}
Methods:
This method generates a tree.
private static int totalNodes = 0;
static Tree GenerateTree()
{
Tree t = new Tree();
t.RootNode = new Node();
t.RootNode.Key = 0;
t.RootNode.Nodes = new List<Node>();
Console.WriteLine(t.RootNode.Key);
List<Node> rootNodes = new List<Node>();
rootNodes.Add(t.RootNode);
while (totalNodes <= 100000)
{
List<Node> newRootNodes = new List<Node>();
foreach (var rootNode in rootNodes)
{
for (int j = 0; j < 3; j++)
{
totalNodes++;
Console.Write(string.Format(" {0}({1}) ", totalNodes, rootNode.Key));
Node childNode = new Node() {Key = totalNodes, Nodes = new List<Node>(), ParentNode = t.RootNode};
rootNode.Nodes.Add(childNode);
newRootNodes.Add(childNode);
}
Console.Write(" ");
}
Console.WriteLine();
rootNodes = newRootNodes;
}
return t;
}
This method is supposed to print a tree, but node is null in some case:
static void PrintTreeParallel(Node rootNode)
{
List<Node> rootNodes = new List<Node>();
List<Node> newRootNodes = new List<Node>();
rootNodes.Add(rootNode);
Console.WriteLine(rootNode.Key);
while (rootNodes.Count > 0)
{
newRootNodes = new List<Node>();
Parallel.ForEach(rootNodes, node =>
{
if (node != null)
{
Console.Write(string.Format(" {0} ", node.Key));
if (node.Nodes != null)
Parallel.ForEach(node.Nodes,
newRoot => { newRootNodes.Add(newRoot); });
}
else
{
//HOW CAN WE GET HERE?????
Debugger.Break();
Console.WriteLine(rootNodes.Count);
}
});
Console.WriteLine();
rootNodes = newRootNodes;
}
}
Execute:
static void Main(string[] args)
{
var t = GenerateTree();
Console.WriteLine("Tree generated");
PrintTreeParallel(t.RootNode);
Console.WriteLine("Tree printed paral");
Console.ReadLine();
}
Question:
What's wrong here?
Why node is null in some case?
And it happens only when there are a lot of generated nodes. For ex if there would be only 10 nodes everything is OK.
The problem is that you have this code:
Parallel.ForEach(node.Nodes, newRoot => { newRootNodes.Add(newRoot); });
Which allows multiple threads to add items to the newRootNodes list concurrently. As a commenter pointed out, List<T> is not thread-safe. What's probably happening is that one thread's Add is being interrupted by another thread's call to Add, which causes an internal index in the list to be incremented. That leaves a null value in one of the list's items.
Then, later in the loop you have:
rootNodes = newRootNodes;
Which puts the corrupted list as the list that's going to be iterated by the while.
You have a data race here:
Parallel.ForEach(node.Nodes,
newRoot => { newRootNodes.Add(newRoot); });
Adding to a list with multiple threads is not thread-safe and will cause undetermined behavior.
First try to run this part with a simple foreach and see if the problem goes away. Running two nested Parallel.ForEach statements is definitely a bizarre choice.
List<T> is indeed not thread safe, so rootNode.Nodes.Add(childNode); is dropping data in unpredictable ways.
Instead of using List<> use ConcurrentBag<> and it will all work. Note that ConcurrentBag<T> is unordered, but that is fine because you have no way of predicting the order from the threads anyway.

Categories