+ 1
Can I use a StringBuilder in a Parallel.ForEachloop
I like to make a concatenate string, in a foreach loop (a parallel.ForEach loop). In the loop there is a lot of code, the concatenated string is just a datacollector. The string is saved for logging purposes. If I do it in my real life application the concatenated string turns out to have most of the data but scattered around in no particular order. When I try is in the playground it seems to work but there is al lot less going on there. https://code.sololearn.com/cLDh9QeOE0Kn How work with stringbuilder in a way that is threadsafe ?
3 Answers
+ 1
using System;
using System.Collections.Concurrent;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace SoloLearn
{
class Program
{
static void Main(string[] args)
{
StringBuilder sb = new StringBuilder();
BlockingCollection<string> fruits = new BlockingCollection<string>();
fruits.Add("Apple");
fruits.Add("Banana");
fruits.Add("Bilberry");
fruits.Add("Blackberry");
fruits.Add("Blackcurrant");
fruits.Add("Blueberry");
fruits.Add("Cherry");
fruits.Add("Coconut");
fruits.Add("Cranberry");
fruits.Add("Date");
fruits.Add("Fig");
fruits.Add("Grape");
fruits.Add("Guava");
fruits.Add("Jack-fruit");
fruits.Add("Kiwi fruit");
fruits.Add("Lemon");
fruits.Add("Lime");
fruits.Add("Lychee");
fruits.Add("Mango");
fruits.Add("Melon");
fruits.Add("Olive");
fruits.Add("Orange");
fruits.Add("Papaya");
fruits.Add("Plum");
fruits.Add("Pineapple");
fruits.Add("Pomegranate");
Parallel.ForEach(fruits, fruit =>
{
Console.WriteLine("Fruit Name: {0}", fruit);
lock(sb) {
sb.Append(fruit).Append("!!").Append(fruit);
}
});
Console.WriteLine(sb.ToString());
}
}
}
+ 1
@Martin Taylor
It's C# not Java.
@sneeze
You really have 2 thread safe issues here. First you're using List<T> which is not thread safe and then StringBuilder which is also not thread safe. When your are reading from the List on another thread the order in which the objects within the list are returned is not guaranteed. Then, when using StringBuilder on another thread from which the object was created, the order to which strings are appended to the StringBuilder is also not guaranteed.
Solving the first problem should be as simple as changing the collection type to an object that is both thread safe and ordered. I think using System.Collections.Concurrent.BlockingCollection<T> should suffice, but I am a bit rusty when it comes to C#.
using System.Collections.Concurrent; // add to your using statements
Change:
List<string> fruits = new List<string>();
To something like:
BlockingCollection<string> fruits = new BlockingCollection<string>();
Next you should be able to use the lock keyword to make the StringBuilder thread safe as well.
lock(sb) {
sb.Append(fruit).Append("!!").Append(fruit);
}
See next post for your altered code:
+ 1
Thank you, I will try.