Parallel.For Loop with Thread-Local Variables - c#

I have a C# ASP.NET MVC project.
I am basically running a simulation (with an option to cancel) and collating the results.
I need to use multi-threading as I could run a million or more simulations at at time.
My code is like this:
public class MyClass
{
private ConcurrentBag<StuffResult> StuffResults { get; set; }
private bool CancellationRequested { get; set; }
public void DoAlotOfStuff(int numberOfStuffToDo)
{
var cancellationTokenSource = new CancellationTokenSource();
var options = new ParallelOptions { CancellationToken = cancellationTokenSource.Token };
Task.Factory.StartNew(() =>
{
if (CancellationRequested) cancellationTokenSource.Cancel();
});
try
{
Parallel.For(0, numberOfStuffToDo, options, a =>
{
options.CancellationToken.ThrowIfCancellationRequested();
var class1 = new Class1();
var class2 = new Class2();
var class3 = new Class3();
var class4 = new Class4(class1, class2, class3);
var result = class4.DoStuff();
StuffResults.Add(result);
});
}
catch (OperationCanceledException e)
{
//handle exception
}
}
}
Question: How can I avoid instantiating a new Class1, Class2, Class3, and Class4 object for each iteration? I read this msdn article but I don't understand it. Perhaps 1 of each object per thread.

It look safe enough to me...
If the classes have some kind of state involved with them then I don't know if you can avoid instantiating them. If not, you might be able to declare the classes outside the loop or make the DoStuff method static so you don't need an instantiated class at all.

I would do it like this:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace ParallelTest
{
class Program
{
static AutoResetEvent autoReset = new AutoResetEvent(false);
static void Main(string[] args)
{
// since this is an async method it will be run in a different thread
DoSomething();
// wait for the async method to signal the main thread
autoReset.WaitOne();
Console.WriteLine("done");
}
async static void DoSomething()
{
// create some common data
const int count = 50000;
const int factor = 3;
// create some tasks
var task1 = Task.Run(() =>
{
int x = 0;
for (int i = 0; i < count * 2; ++i)
{
x += i + factor * 3;
Console.WriteLine("task1: " + i + factor * 3);
}
return x;
});
var task2 = Task.Run(() =>
{
int x = 0;
for (int i = 0; i < count * 2; ++i)
{
x += i + factor * 4;
Console.WriteLine("task2: " + i + factor * 4);
}
return x;
});
var task3 = Task.Run(() =>
{
int x = 0;
for (int i = 0; i < count * 2; ++i)
{
x += i + factor * 5;
Console.WriteLine("task3: " + i + factor * 5);
}
return x;
});
// create a resulttask which will run all the tasks in parallel
var resultTask = Task.WhenAll(task1, task2, task3);
// start the task and wait till it finishes
var results = await resultTask;
// display the results
for (int i = 0; i < results.Length; ++i)
{
Console.WriteLine("result" + i + ": " + results[i]);
}
// signal main thread
autoReset.Set();
}
}
}

Related

Calculate FLOPS and IOPS

I want to calculate IOPS and FLOPS in a 5 min test, where each runs for 150 seconds. However, I am getting results that are surprising, where the average IOPS on 16 threads is 53986225 with a standard deviation of 1282739 for example. Am I doing something wrong below? Also please point me to any optimizations as well.
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading;
namespace Threads
{
class Program
{
// Count of operations done per thread
private static long[] operations;
// Volatile variable to signal threads to stop
private static volatile bool stop = false;
private const float TestTimeInMinutes = 1f;
private const float SleepInSeconds = 1f;
// Handles used by threads to signal to it's caller it's done
private static ManualResetEvent[] handles;
static void Main(string[] args)
{
// Get optimal number of threads
var tNum = Environment.ProcessorCount;
// Ask user for thread count
while (true)
{
Console.Write($"Please enter number of threads or 0 for default {tNum}: ");
var input = Console.ReadLine();
if (int.TryParse(input, out var _tNum))
{
if (_tNum != 0)
{
tNum = _tNum;
}
break;
}
}
// Initialize operations array
operations = new long[tNum];
// Initialize handles array
handles = new ManualResetEvent[tNum];
// Populate handles
for (var n = 0; n < tNum; n++)
{
handles[n] = new ManualResetEvent(false);
}
long totalIopS = 0;
long totalFlopS = 0;
long IopDev = 0;
long FlopDev = 0;
// Stopwatch to measure time
var stopwatch = new Stopwatch();
for (int i = 0; i < 5; i++)
{
Console.WriteLine($"Test {i+1}");
stopwatch.Restart();
var iop = MeasureIOP(tNum);
Console.WriteLine($"IOPS: {iop/30}\nElapsed: {stopwatch.Elapsed:mm\\:ss\\.fff}");
totalIopS += iop;
IopDev += StandardDeviation(operations);
stopwatch.Restart();
var flop = MeasureFLOP(tNum);
Console.WriteLine($"FLOPS: {flop/30}\nElapsed: {stopwatch.Elapsed:mm\\:ss\\.fff}");
totalFlopS += flop;
FlopDev += StandardDeviation(operations);
}
Console.WriteLine($"Average\nIOPS: {totalIopS/150}\nIOP Standard Deviation: {(int)(IopDev/150)}\nFLOP: {totalFlopS/150}\nFLOP Standard Deviation: {(int)(FlopDev/150)}");
Console.Write("The Test has ended");
var finalInput = Console.ReadLine();
}
private static void FloatOp(object index)
{
float floatOp = 0;
var i = (int) index;
long op = 0;
while (!stop)
{
floatOp = (floatOp + 0.8f)-0.10f*0.2f;
op++;
}
operations[i] = op;
// Signal that the thread is done
handles[i].Set();
}
private static long MeasureFLOP(int tNum)
{
const int numberOfTries = (int) ((TestTimeInMinutes / 2 * 60f) / SleepInSeconds);
long flop = 0;
for (var i = 0; i < numberOfTries; i++)
{
stop = false;
for (var threadIndex = 0; threadIndex < tNum; threadIndex++)
{
// Reset the handle
// By resetting the handle, the thread is considered active
handles[threadIndex].Reset();
// Start a thread
ThreadPool.QueueUserWorkItem(FloatOp, threadIndex);
}
Thread.Sleep((int)(SleepInSeconds*1000));
stop = true;
// Wait for all threads to exit
WaitHandle.WaitAll(handles);
// Calculate flops
flop += (int)((operations.Sum()/tNum)/SleepInSeconds);
}
return flop / numberOfTries;
}
private static void IntOp(object index)
{
long intop = 0;
var i = (int) index;
long op = 0;
while (!stop)
{
intop = intop + 1 - 10 * 2;
op++;
}
operations[i] = op;
// Signal that the thread is done
handles[i].Set();
}
private static long MeasureIOP(int tNum)
{
const int numberOfTries = (int) ((TestTimeInMinutes / 2 * 60f) / SleepInSeconds);
long iop = 0;
for (var i = 0; i < numberOfTries; i++)
{
stop = false;
for (var threadIndex = 0; threadIndex < tNum; threadIndex++)
{
// Reset the handle
// By resetting the handle, the thread is considered active
handles[threadIndex].Reset();
ThreadPool.QueueUserWorkItem(IntOp, threadIndex);
}
Thread.Sleep((int)(SleepInSeconds*1000));
stop = true;
// Wait for all threads to exit
WaitHandle.WaitAll(handles);
// Calculate iop
iop += (int)((operations.Sum()/tNum)/SleepInSeconds);
}
return iop / numberOfTries;
}
private static long StandardDeviation(long[] nums)
{
var average = nums.Average();
var sum = nums.Sum(n => Math.Pow(n - average, 2));
return (long)Math.Sqrt(sum / nums.Length);
}
}
}

Who is waiting for a ManualResetEvent?

Is there a simple way to check if any threads are waiting for a specific ManualResetEvent
object to be set? It seems to me that the computer must be somehow keeping a queue of the
paused threads to restore later, so I'm just hoping to do the obvious and access
this list (just to check whether or not it is empty)...but I couldn't find a
method from looking at Microsoft's related Threading/WaitHandle/Monitor help pages.
Test code is below. I'd simply like to replace the uglyWaiters counter.
using System;
using System.Threading;
namespace demo
{
class Program
{
static void Main()
{
var gate = new ManualResetEvent(false);
int uglyWaiters = 0; // I want to stop using this variable and instead use something built into ManualResetEvent
new Thread(() =>
{
for (int i = 0; i < 5; i++)
{
Console.WriteLine("a" + i);
uglyWaiters++;
gate.WaitOne();
uglyWaiters--;
Thread.Sleep(400);
}
}).Start();
new Thread(() =>
{
for (int i = 0; i < 5; i++)
{
Console.WriteLine("b" + i);
uglyWaiters++;
gate.WaitOne();
uglyWaiters--;
Thread.Sleep(500);
}
}).Start();
for (int count = 0; count < 15; count++)
{
Console.WriteLine(uglyWaiters + " threads waiting at t=" + count*230 + "ms");
gate.Set(); Thread.Sleep(30); gate.Reset();
Thread.Sleep(200);
}
Console.ReadLine();
}
}
}

Multiple threads calling same method in C#

I have the following C# code snippet in which I have simulated my problem. In this program I have a Service function that call ReadRooms method.
Now I am calling the service method on different threads. I was expecting that both ServiceCall and ReadRooms method will fired equally but I am getting below result that is not correct.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace ConsoleApplication1
{
class Program
{
public static void ReadRooms(int i)
{
Console.WriteLine("Reading Room::" + i);
Thread.Sleep(2000);
}
public static void CallService(int i)
{
Console.WriteLine("ServiceCall::" + i);
ReadRooms(i);
}
static void Main(string[] args)
{
Thread[] ts = new Thread[4];
for (int i = 0; i < 4; i++)
{
ts[i] = new Thread(() =>
{
int temp = i;
CallService(temp);
});
ts[i].Start();
}
for (int i = 0; i < 4; i++)
{
ts[i].Join();
}
Console.WriteLine("done");
Console.Read();
}
}
}
You are still 'capturing the loop variable'. You are creating a temp but too late, when i is already captured.
Try this:
for (int i = 0; i < 4; i++)
{
int temp = i; // outside the lambda
ts[i] = new Thread(() =>
{
//int temp = i; // not here
CallService(temp);
});
ts[i].Start();
}
You should put this line
int temp = i;
before the Thread creation
for (int i = 0; i < 4; i++)
{
int temp = i;
ts[i] = new Thread(() => CallService(temp));
ts[i].Start();
}
This way you will create a local copy of i that will be used by the lambda expression.
Your thread action is closing over the variable i instead of its current value. You therefore have a race between the thread reading i and the increment in the for loop. You can pass it as a parameter instead:
ts[i] = new Thread(index =>
{
CallService((int)index);
});
ts[i].Start(i);
alternatively you can move the copy of temp to inside the loop instead of the thread action:
for (int i = 0; i < 4; i++)
{
int temp = i;
ts[i] = new Thread(() =>
{
CallService(temp);
});
ts[i].Start();
}

Why is my disruptor example so slow?

I've taken the code example from Stack Overflow question Disruptor.NET example and modified it to "measure" time. Full listing is below:
using System;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Disruptor;
using Disruptor.Dsl;
namespace DisruptorTest
{
public sealed class ValueEntry
{
public long Value { get; set; }
public ValueEntry()
{
Console.WriteLine("New ValueEntry created");
}
}
public class ValueAdditionHandler : IEventHandler<ValueEntry>
{
public void OnNext(ValueEntry data, long sequence, bool endOfBatch)
{
Program.sw.Stop();
long microseconds = Program.sw.ElapsedTicks / (Stopwatch.Frequency / (1000L * 1000L));
Console.WriteLine("elapsed microseconds = " + microseconds);
Console.WriteLine("Event handled: Value = {0} (processed event {1}", data.Value, sequence);
}
}
class Program
{
public static Stopwatch sw = Stopwatch.StartNew();
private static readonly Random _random = new Random();
private static readonly int _ringSize = 16; // Must be multiple of 2
static void Main(string[] args)
{
var disruptor = new Disruptor.Dsl.Disruptor<ValueEntry>(() => new ValueEntry(), _ringSize, TaskScheduler.Default);
disruptor.HandleEventsWith(new ValueAdditionHandler());
var ringBuffer = disruptor.Start();
while (true)
{
var valueToSet = _random.Next();
long sequenceNo = ringBuffer.Next();
ValueEntry entry = ringBuffer[sequenceNo];
entry.Value = valueToSet;
sw.Restart();
ringBuffer.Publish(sequenceNo);
Console.WriteLine("Published entry {0}, value {1}", sequenceNo, entry.Value);
Thread.Sleep(1000);
}
}
}
}
And the output is:
New ValueEntry created
New ValueEntry created
New ValueEntry created
New ValueEntry created
New ValueEntry created
New ValueEntry created
New ValueEntry created
New ValueEntry created
New ValueEntry created
New ValueEntry created
New ValueEntry created
New ValueEntry created
New ValueEntry created
New ValueEntry created
New ValueEntry created
New ValueEntry created
Published entry 0, value 1510145842
elapsed microseconds = 2205
Event handled: Value = 1510145842 (processed event 0
Published entry 1, value 1718075893
elapsed microseconds = 85
Event handled: Value = 1718075893 (processed event 1
Published entry 2, value 1675907645
elapsed microseconds = 32
Event handled: Value = 1675907645 (processed event 2
Published entry 3, value 1563009446
elapsed microseconds = 75
Event handled: Value = 1563009446 (processed event 3
Published entry 4, value 1782914062
elapsed microseconds = 34
Event handled: Value = 1782914062 (processed event 4
Published entry 5, value 1516398244
elapsed microseconds = 50
Event handled: Value = 1516398244 (processed event 5
Published entry 6, value 76829327
elapsed microseconds = 50
Event handled: Value = 76829327 (processed event 6
So it takes about 50 microseconds to pass data from one thread to another. But it is not fast at all! "The current version of the Disruptor can do ~50 ns between threads at a rate of 1 million messages per second." So my results are 1000 times slower than expected.
What's wrong with my example and how do achieve 50 ns speed?
I've modified program above and now receive 1 microsecond delay, which is much better. However, I am still waiting for the response from disruptor pattern experts. I'm looking for an example which can prove that I can actually pass data in 50 ns.
Also I wrote the same test using BlockingCollection and received 14 microseconds in average, which proves that Disruptor is faster:
Using BlockingCollection:
average = 14 minimum = 0 0-5 = 890558, 5-10 = 1773781, 10-30 = 6900128, >30 = 435433
Using Disruptor:
average = 0 minimum = 0 0-5 = 9908469, 5-10 = 64464, 10-30 = 19902, >30 = 7065
BlockingCollection code:
using System;
using System.Collections.Concurrent;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace DisruptorTest
{
public sealed class ValueEntry
{
public int Value { get; set; }
public ValueEntry()
{
// Console.WriteLine("New ValueEntry created");
}
}
//public class ValueAdditionHandler : IEventHandler<ValueEntry>
//{
// public void OnNext(ValueEntry data, long sequence, bool endOfBatch)
// {
// long microseconds = Program.sw[data.Value].ElapsedTicks / (Stopwatch.Frequency / (1000L * 1000L));
// Program.results[data.Value] = microseconds;
// //Console.WriteLine("elapsed microseconds = " + microseconds);
// //Console.WriteLine("Event handled: Value = {0} (processed event {1}", data.Value, sequence);
// }
//}
class Program
{
public const int length = 10000000;
public static Stopwatch[] sw = new Stopwatch[length];
public static long[] results = new long[length];
static BlockingCollection<ValueEntry> dataItems = new BlockingCollection<ValueEntry>(150);
static void Main(string[] args)
{
for (int i = 0; i < length; i++)
{
sw[i] = Stopwatch.StartNew();
}
// A simple blocking consumer with no cancellation.
Task.Factory.StartNew(() =>
{
while (!dataItems.IsCompleted)
{
ValueEntry ve = null;
try
{
ve = dataItems.Take();
long microseconds = sw[ve.Value].ElapsedTicks / (Stopwatch.Frequency / (1000L * 1000L));
results[ve.Value] = microseconds;
//Console.WriteLine("elapsed microseconds = " + microseconds);
//Console.WriteLine("Event handled: Value = {0} (processed event {1}", ve.Value, ve.Value);
}
catch (InvalidOperationException) { }
}
}, TaskCreationOptions.LongRunning);
for (int i = 0; i < length; i++)
{
var valueToSet = i;
ValueEntry entry = new ValueEntry();
entry.Value = valueToSet;
sw[i].Restart();
dataItems.Add(entry);
//Console.WriteLine("Published entry {0}, value {1}", valueToSet, entry.Value);
//Thread.Sleep(1000);
}
// Wait until all events are delivered
Thread.Sleep(5000);
long average = 0;
long minimum = 10000000000;
int firstFive = 0;
int fiveToTen = 0;
int tenToThirty = 0;
int moreThenThirty = 0;
// Do not count first 100 items because they could be extremely slow
for (int i = 100; i < length; i++)
{
average += results[i];
if (results[i] < minimum)
{
minimum = results[i];
}
if (results[i] < 5)
{
firstFive++;
}
else if (results[i] < 10)
{
fiveToTen++;
}
else if (results[i] < 30)
{
tenToThirty++;
} else
{
moreThenThirty++;
}
}
average /= (length - 100);
Console.WriteLine("average = {0} minimum = {1} 0-5 = {2}, 5-10 = {3}, 10-30 = {4}, >30 = {5}", average, minimum, firstFive, fiveToTen, tenToThirty, moreThenThirty);
}
}
}
Disruptor code:
using System;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Disruptor;
using Disruptor.Dsl;
namespace DisruptorTest
{
public sealed class ValueEntry
{
public int Value { get; set; }
public ValueEntry()
{
// Console.WriteLine("New ValueEntry created");
}
}
public class ValueAdditionHandler : IEventHandler<ValueEntry>
{
public void OnNext(ValueEntry data, long sequence, bool endOfBatch)
{
long microseconds = Program.sw[data.Value].ElapsedTicks / (Stopwatch.Frequency / (1000L * 1000L));
Program.results[data.Value] = microseconds;
//Console.WriteLine("elapsed microseconds = " + microseconds);
//Console.WriteLine("Event handled: Value = {0} (processed event {1}", data.Value, sequence);
}
}
class Program
{
public const int length = 10000000;
public static Stopwatch[] sw = new Stopwatch[length];
public static long[] results = new long[length];
private static readonly Random _random = new Random();
private static readonly int _ringSize = 1024; // Must be multiple of 2
static void Main(string[] args)
{
for (int i = 0; i < length; i++)
{
sw[i] = Stopwatch.StartNew();
}
var disruptor = new Disruptor.Dsl.Disruptor<ValueEntry>(() => new ValueEntry(), _ringSize, TaskScheduler.Default);
disruptor.HandleEventsWith(new ValueAdditionHandler());
var ringBuffer = disruptor.Start();
for (int i = 0; i < length; i++)
{
var valueToSet = i;
long sequenceNo = ringBuffer.Next();
ValueEntry entry = ringBuffer[sequenceNo];
entry.Value = valueToSet;
sw[i].Restart();
ringBuffer.Publish(sequenceNo);
//Console.WriteLine("Published entry {0}, value {1}", sequenceNo, entry.Value);
//Thread.Sleep(1000);
}
// wait until all events are delivered
Thread.Sleep(5000);
long average = 0;
long minimum = 10000000000;
int firstFive = 0;
int fiveToTen = 0;
int tenToThirty = 0;
int moreThenThirty = 0;
// Do not count first 100 items because they could be extremely slow
for (int i = 100; i < length; i++)
{
average += results[i];
if (results[i] < minimum)
{
minimum = results[i];
}
if (results[i] < 5)
{
firstFive++;
}
else if (results[i] < 10)
{
fiveToTen++;
}
else if (results[i] < 30)
{
tenToThirty++;
}
else
{
moreThenThirty++;
}
}
average /= (length - 100);
Console.WriteLine("average = {0} minimum = {1} 0-5 = {2}, 5-10 = {3}, 10-30 = {4}, >30 = {5}", average, minimum, firstFive, fiveToTen, tenToThirty, moreThenThirty);
}
}
}
Here, I fixed your code:
using System;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Disruptor;
using Disruptor.Dsl;
namespace DisruptorTest
{
public sealed class ValueEntry
{
public int Value { get; set; }
public ValueEntry()
{
// Console.WriteLine("New ValueEntry created");
}
}
class Program
{
public const int length = 1000000;
public static Stopwatch sw;
private static readonly Random _random = new Random();
private static readonly int _ringSize = 1024; // Must be multiple of 2
static void Main(string[] args)
{
sw = Stopwatch.StartNew();
var disruptor = new Disruptor.Dsl.Disruptor<ValueEntry>(() => new ValueEntry(), _ringSize, TaskScheduler.Default);
var ringBuffer = disruptor.Start();
for (int i = 0; i < length; i++)
{
var valueToSet = i;
long sequenceNo = ringBuffer.Next();
ValueEntry entry = ringBuffer[sequenceNo];
entry.Value = valueToSet;
ringBuffer.Publish(sequenceNo);
//Console.WriteLine("Published entry {0}, value {1}", sequenceNo, entry.Value);
//Thread.Sleep(1000);
}
var elapsed = sw.Elapsed.Miliseconds();
// wait until all events are delivered
Thread.Sleep(10000);
double average = /(double)length;
Console.WriteLine("average = " + average);
}
}
}
This should correctly test how long does it take for each item.
I read the BlockingCollecton code, You add many Console.WriteLine in Disruptor but no one in BlockingCollection, Console.WriteLine is slow, it have a lock inside.
Your RingBufferSize is too small, this effects performance, shoule be 1024 or larger.
and while (!dataItems.IsCompleted) may have some problem, BlockCollection isn't always in adding state, it will cause thread ends early.
Task.Factory.StartNew(() => {
while (!dataItems.IsCompleted)
{
ValueEntry ve = null;
try
{
ve = dataItems.Take();
long microseconds = sw[ve.Value].ElapsedTicks / (Stopwatch.Frequency / (1000L * 1000L));
results[ve.Value] = microseconds;
//Console.WriteLine("elapsed microseconds = " + microseconds);
//Console.WriteLine("Event handled: Value = {0} (processed event {1}", ve.Value, ve.Value);
}
catch (InvalidOperationException) { }
}
}, TaskCreationOptions.LongRunning);
for (int i = 0; i < length; i++)
{
var valueToSet = i;
ValueEntry entry = new ValueEntry();
entry.Value = valueToSet;
sw[i].Restart();
dataItems.Add(entry);
//Console.WriteLine("Published entry {0}, value {1}", valueToSet, entry.Value);
//Thread.Sleep(1000);
}
I have rewrite you code, Disruptor is 10x faster than BlockingCollection with multi producer (10 parallel producet), 2x faster than BlockingCollection with Single producer:
using System;
using System.Collections.Concurrent;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;
using Disruptor;
using Disruptor.Dsl;
using NUnit.Framework;
namespace DisruptorTest.Ds
{
public sealed class ValueEntry
{
internal int Id { get; set; }
}
class MyHandler : IEventHandler<ValueEntry>
{
public void OnEvent(ValueEntry data, long sequence, bool endOfBatch)
{
}
}
[TestFixture]
public class DisruptorPerformanceTest
{
private volatile bool collectionAddEnded;
private int producerCount = 10;
private int runCount = 1000000;
private int RingBufferAndCapacitySize = 1024;
[TestCase()]
public async Task TestBoth()
{
for (int i = 0; i < 1; i++)
{
foreach (var rs in new int[] {64, 512, 1024, 2048 /*,4096,4096*2*/})
{
Console.WriteLine($"RingBufferAndCapacitySize:{rs}, producerCount:{producerCount}, runCount:{runCount} of {i}");
RingBufferAndCapacitySize = rs;
await DisruptorTest();
await BlockingCollectionTest();
}
}
}
[TestCase()]
public async Task BlockingCollectionTest()
{
var sw = new Stopwatch();
BlockingCollection<ValueEntry> dataItems = new BlockingCollection<ValueEntry>(RingBufferAndCapacitySize);
sw.Start();
collectionAddEnded = false;
// A simple blocking consumer with no cancellation.
var task = Task.Factory.StartNew(() =>
{
while (!collectionAddEnded && !dataItems.IsCompleted)
{
//if (!dataItems.IsCompleted && dataItems.TryTake(out var ve))
if (dataItems.TryTake(out var ve))
{
}
}
}, TaskCreationOptions.LongRunning);
var tasks = new Task[producerCount];
for (int t = 0; t < producerCount; t++)
{
tasks[t] = Task.Run(() =>
{
for (int i = 0; i < runCount; i++)
{
ValueEntry entry = new ValueEntry();
entry.Id = i;
dataItems.Add(entry);
}
});
}
await Task.WhenAll(tasks);
collectionAddEnded = true;
await task;
sw.Stop();
Console.WriteLine($"BlockingCollectionTest Time:{sw.ElapsedMilliseconds/1000d}");
}
[TestCase()]
public async Task DisruptorTest()
{
var disruptor =
new Disruptor.Dsl.Disruptor<ValueEntry>(() => new ValueEntry(), RingBufferAndCapacitySize, TaskScheduler.Default,
producerCount > 1 ? ProducerType.Multi : ProducerType.Single, new BlockingWaitStrategy());
disruptor.HandleEventsWith(new MyHandler());
var _ringBuffer = disruptor.Start();
Stopwatch sw = Stopwatch.StartNew();
sw.Start();
var tasks = new Task[producerCount];
for (int t = 0; t < producerCount; t++)
{
tasks[t] = Task.Run(() =>
{
for (int i = 0; i < runCount; i++)
{
long sequenceNo = _ringBuffer.Next();
_ringBuffer[sequenceNo].Id = 0;
_ringBuffer.Publish(sequenceNo);
}
});
}
await Task.WhenAll(tasks);
disruptor.Shutdown();
sw.Stop();
Console.WriteLine($"DisruptorTest Time:{sw.ElapsedMilliseconds/1000d}s");
}
}
}
BlockingCollectionTest with a shared ValueEntry instance (no new ValueEntry() in for loop)
RingBufferAndCapacitySize:64, producerCount:10, runCount:1000000 of 0
DisruptorTest Time:16.962s
BlockingCollectionTest Time:18.399
RingBufferAndCapacitySize:512, producerCount:10, runCount:1000000 of 0
DisruptorTest Time:6.101s
BlockingCollectionTest Time:19.526
RingBufferAndCapacitySize:1024, producerCount:10, runCount:1000000 of 0
DisruptorTest Time:2.928s
BlockingCollectionTest Time:20.25
RingBufferAndCapacitySize:2048, producerCount:10, runCount:1000000 of 0
DisruptorTest Time:2.448s
BlockingCollectionTest Time:20.649
BlockingCollectionTest create a new ValueEntry() in for loop
RingBufferAndCapacitySize:64, producerCount:10, runCount:1000000 of 0
DisruptorTest Time:27.374s
BlockingCollectionTest Time:21.955
RingBufferAndCapacitySize:512, producerCount:10, runCount:1000000 of 0
DisruptorTest Time:5.011s
BlockingCollectionTest Time:20.127
RingBufferAndCapacitySize:1024, producerCount:10, runCount:1000000 of 0
DisruptorTest Time:2.877s
BlockingCollectionTest Time:22.656
RingBufferAndCapacitySize:2048, producerCount:10, runCount:1000000 of 0
DisruptorTest Time:2.384s
BlockingCollectionTest Time:23.567
https://www.cnblogs.com/darklx/p/11755686.html

Multithreading and ProgressBar

Need to make an multithreading application, which performs some activities and shows progress of all work in ProgressBar. Here is the code. Only thread named "thread - 0" makes all work. I need to distribute work between all threads.
public partial class Form1 : Form
{
const float maxWorkedValue = 10;
const int threadsCount = 5;
float totalProgress;
int threadSleepTime = 1;
object locker = new object();
List<Thread> threads = new List<Thread>();
public Form1()
{
InitializeComponent();
prBar.Maximum = (int)maxWorkedValue;
prBar.Step = 1;
for (int i = 0; i < threadsCount; i++)
{
threads.Add(new Thread(BeginCalculate) { Name = "Thread - " + i.ToString() });
threads[i].Start();
}
}
void BeginCalculate()
{
for (int i = 0; i < maxWorkedValue; i++)
{
lock (locker)
{
float currentProgress = CalculateLongTask(i);
Update((currentProgress / maxWorkedValue) * 100, Thread.CurrentThread.Name);
Thread.Sleep(threadSleepTime);
}
}
}
float CalculateLongTask(int value)
{
for (int i=0; i<value;i++)
Thread.Sleep(threadSleepTime);
return totalProgress++;
}
void Update(float i, string threadName)
{
if (InvokeRequired)
{
BeginInvoke(new Action<float, string>(Update), new object[] { i, threadName });
return;
}
label1.Text = threadName;
prBar.Value = (int)i;
}
}
UPDATE2:
I've updated the code above. But it not resolved the problem. How to synchronize the variable "i" between different threads? An exception (progressbar.Max) is thrown. Why?
for (int i = 0; i < maxWorkedValue; i++)
{
lock (locker)
{
float currentProgress = CalculateLongTask(i);
Update((currentProgress / maxWorkedValue) * 100, Thread.CurrentThread.Name);
Thread.Sleep(15);
}
}
EDIT:
If I understood you correctly you want to execute a loop (i.e.for (int i = 0; i < maxWorkedValue; i++) ) concurrently among many threads.
This is extremely easier to accomplish using the Parallel extensions
1.
Here's your modified code:
public partial class Form1 : Form
{
const float maxWorkedValue = 100;
const int threadCount = 5;
float maxWorkedValue2 = 1;
Thread thread;
public Form1()
{
InitializeComponent();
prBar.Maximum = (int)maxWorkedValue;
prBar.Step = 1;
thread = new Thread(BeginCalculate) { Name = "Father thread" };
thread.Start();
}
void BeginCalculate()
{
Parallel.For(0, (int)maxWorkedValue,
(i) =>
{
float currentProgress = CalculateLongTask(i);
// here we are (probably) in a thread child
//of "Father thread" so let's use ManagedThreadId instead of the name...
Update((currentProgress / maxWorkedValue) * 100, Thread.CurrentThread.ManagedThreadId.ToString());
Thread.Sleep(2);
return;
});
}
float CalculateLongTask(int value)
{
for (int i = 0; i < value; i++)
Thread.Sleep(15);
return maxWorkedValue2++;
}
void Update(float i, string threadName)
{
if (InvokeRequired)
{
BeginInvoke(new Action<float, string>(Update), new object[] { i, threadName });
return;
}
label1.Text = threadName;
prBar.Value = Math.Min((int)i, 100);
}
}
1
If you're using .NET 4.0 Parallel extensions comes with the framework.
If you're using .NET 3.5, look at this Q&A to know how to use it.
Mostly, your locking scheme is broken:
void BeginCalculate() // main thread method
{
lock (locker)
{
.... // 1 thread at a time
}
}
All threads are run in sequence, you're not really multi-threading.
I can't comment on the rest except to say it doesn't look like something you should use for real.
You should probably outline what you really need to do, It won't be Sleep().

Categories