Reputation: 87
I have created a struct, say AStruct
, and override its ToString()
method. Then I have written a parallel for to return some AStruct
and put them in a list so I can use a StreamWriter
to output them, like
StreamWriter sw = new StreamWriter(@"ABC.txt");
StringBuilder sb = new StringBuilder();
List<AStruct> AList = new List<AStruct>();
Parallel.For(0,10,i => // generate 10 AStruct(s)
{
AList.Add(DoSomethingThatReturnsAStruct);
});
for(int i =0; i< AList.Count();i++) //put in a StringBuilder
{
sb.AppendLine(AList[i].ToString());
}
sw.Write(sb.ToString());
sw.Close();
The problem is the output file only print 7/8 lines of AList, while AList actually got all the 10 elements. I wonder does this relate to thread-safety of StringBuilder. Could someone explain why not all lines are output?
Upvotes: 1
Views: 4220
Reputation:
In your code above the instance of StringBuilder
is never accessed/modified by anything other than the main
thread (or whatever thread is creating the sw
) so thread-safety of StringBuilder
is not relevant however you have a much bigger bug one that you should always remember when dealing with more than one thread.
Never ever modify a shared resource by multiple threads unless that resource is thread-safe
You are updating the AList
from different threads so either use a lock
to synchronize access or a thread-safe collection e.g. ConcurrentQueue
(order guaranteed) or ConcurrentBag
(order not guaranteed)
You are also adding 9 entries to the AList
instead of 10.
Finally here's your modified code which yields the expected result.
var sw = new StreamWriter(@"ABC.txt");
try
{
var AList = new List<AStruct>();
var locker = new object();
Parallel.For(0, 10, i => // generate 10 AStruct(s)
{
lock (locker) { AList.Add(new AStruct()); }
});
var sb = new StringBuilder();
for (int i = 0; i < AList.Count; i++) //put in a StringBuilder
{
sb.AppendLine(AList[i].ToString());
}
sw.Write(sb.ToString());
} finally
{
sw.Close();
}
Upvotes: 4