This version doesn't use PLINQ, it uses the standard LINQ, so it's safe.
This version uses an overload of
Aggregate()
that's not safely parallelizable, so it will also execute on a single thread. This means it's safe, but it also won't be any faster than the sequential version.To actually take advantage of PLINQ, you would need to use another overload of
Aggregate()
that actually can execute in parallel. In your case that would mean having a separateStringBuilder
for each thread and then combine all theStringBuilder
s into one. Something like:input.AsParallel().Aggregate( () => new StringBuilder(), (text, current) => text.AppendFormat("@{0}", current.FullName), (text1, text2) => text1.Append(text2), text => text.ToString())
This assumes that you don't care about the order of elements in the final string. If you do, this code won't work correctly.
This code modifies the same
StringBuilder
object from multiple threads.StringBuilder
is not thread-safe, so this code is not safe.
Plinq Aggregate extension with StringBuilder
-
30-08-2022 - |
Pregunta
I've made some experimental Plinq query, and I'm not sure about the result can be corrupted or not.
Here is 3 different method, which provides the same result:
// unitTask is typeof Task<List<SomeEntity>>
//sequential version PLINQ
Console.WriteLine(unitTask.Result.Take(10)
.Aggregate(new StringBuilder(),
(text, current) => text.AppendFormat("@{0}sa{1}",
current.FullName.Substring(0, 3),
current.FullName.Substring(4)))
.ToString());
//parallel version PLINQ
Console.WriteLine(unitTask.Result.Take(10).AsParallel()
.Aggregate(new StringBuilder(),
(text, current) => text.AppendFormat("@{0}sa{1}",
current.FullName.Substring(0, 3),
current.FullName.Substring(4)))
.ToString());
//parallel version foreach with Partitioner
var output = new StringBuilder();
Parallel.ForEach(Partitioner.Create(unitTask.Result.Take(10)), r =>
{
//Console.WriteLine(Thread.CurrentThread.ManagedThreadId);
output.AppendFormat("@{0}sa{1}", r.FullName.Substring(0, 3),
r.FullName.Substring(4));
});
Console.WriteLine(output.ToString());
My questions are:
Can I use StringBuilder
in PLINQ?
Due to append method is not thread safe as I know.
Or does it run in sequential mode in this case?
Parallel.Foreach runs the query in different threads, but result is the same as sequential Plinq.
Is it accidental, or it's smart and uses some synchronization?
Solución
Otros consejos
It's accidental, probably because the code doesn't do all that much and may be running on a single thread. All your calls involde unitTask.Result
which blocks until unitTask
finishes. All snippets actually work on only 10 entities produced sequentially, so there is not enough data to justify parallel execution
The three snippets do different things:
- The first snippet simply processes a list of 10 snippets sequentially.
- The PLINQ version takes a list of 10 entities but doesn't do anything in parallel. Even if it did, the call to
Aggregate
collects the results from all workers and processes them sequentially to create the final result. - The third snippet could display parallel behavior, as it executes an action block in parallel. Again, the number of results is too small and only one thread is used