Hi I would like to know if there is something similiar to the await statement, which is used with tasks, which I can implement with threads in c#?
What I want to do is:
Start Thread A, compute some data and put the result on variable x.
After that variable x is transferred to another thread B and at the same time
Thread A starts again to compute some data, while thread B starts another calculation with the result x.
UPDATE: Ok there seems to be some confusion so I will be more accurate in my description:
I use two sensors which produce data. The data needs to be retrieved in such a way that SensorA data is retrieved (which takes a long time) and immediately after that the data from SensorB must be retrieved in another Thread, while SensorA continues retrieving another data block. The problem is i cant queue the data of both sensors in the same queue, but I need to store the data of both sensor in ONE data structure/object.
My idea was like that:
Get Data from Sensor A in Thread A.
Give result to Thread B and restart Thread A.
While Thread A runs again Thread B gets Data from Sensor B and computes the data from Sensor A and B
You can assume that Thread A always needs a longer time than Thread B
As I said in a comment. This looks like classic Producer/Consumer, for which we can use e.g. a BlockingCollection.
This is a slight modification of the sample from that page:
BlockingCollection<Data> dataItems = new BlockingCollection<Data>(100);
// "Thread B"
Task.Run(() =>
{
while (!dataItems.IsCompleted)
{
Data dataA = null;
try
{
dataA = dataItems.Take();
}
catch (InvalidOperationException) { }
if (dataA != null)
{
var dataB = ReadSensorB();
Process(dataA,dataB);
}
}
Console.WriteLine("\r\nNo more items to take.");
});
// "Thread A"
Task.Run(() =>
{
while (moreItemsToAdd)
{
Data dataA = ReadSensorA();
dataItems.Add(dataA);
}
// Let consumer know we are done.
dataItems.CompleteAdding();
});
And then moreItemsToAdd is just whatever code you need to have to cope with needing to shut this process down.
I'm not sure why you're avoiding the use of tasks? Maybe you're on an older version of .net? If so, BlockingCollection as Damien suggested is also not an option. If you're using "normal" threads, you can use a waithandle to signal results between threads. For example, an AutoResetEvent.
private int a;
private AutoResetEvent newResult = new AutoResetEvent(false);
private void ThreadA()
{
while (true)
{
a = GetSensorA();
newResult.Set();
}
}
private void ThreadB()
{
int b;
while (true)
{
newResult.WaitOne();
b = GetSensorB(); // or before "waitone"
Console.WriteLine(a + b); // do something
}
}
edit: had slight mistake in there with the reset, thanks for pointing out Damien - updated
If you can use .Net 4.5 or later, then the best way to approach this is to use the DataFlow component of the TPL.
(You must use NuGet to install DataFlow; it's not part of the CLR by default.)
Here's a sample compilable console application which demonstrates how to use DataFlow to do it:
using System;
using System.Threading;
using System.Threading.Tasks;
using System.Threading.Tasks.Dataflow;
namespace SensorDemo
{
public sealed class SensorAData
{
public int Data;
}
public sealed class SensorBData
{
public double Data;
}
public sealed class SensorData
{
public SensorAData SensorAData;
public SensorBData SensorBData;
public override string ToString()
{
return $"SensorAData = {SensorAData.Data}, SensorBData = {SensorBData.Data}";
}
}
class Program
{
static void Main()
{
var sensorADataSource = new TransformBlock<SensorAData, SensorData>(
sensorAData => addSensorBData(sensorAData),
dataflowOptions());
var combinedSensorProcessor = new ActionBlock<SensorData>(
data => process(data),
dataflowOptions());
sensorADataSource.LinkTo(combinedSensorProcessor, new DataflowLinkOptions { PropagateCompletion = true });
// Create a cancellation source that will cancel after a few seconds.
var cancellationSource = new CancellationTokenSource(delay:TimeSpan.FromSeconds(20));
Task.Run(() => continuouslyReadFromSensorA(sensorADataSource, cancellationSource.Token));
Console.WriteLine("Started reading from SensorA");
sensorADataSource.Completion.Wait(); // Wait for reading from SensorA to complete.
Console.WriteLine("Completed reading from SensorA.");
combinedSensorProcessor.Completion.Wait();
Console.WriteLine("Completed processing of combined sensor data.");
}
static async Task continuouslyReadFromSensorA(TransformBlock<SensorAData, SensorData> queue, CancellationToken cancellation)
{
while (!cancellation.IsCancellationRequested)
await queue.SendAsync(readSensorAData());
queue.Complete();
}
static SensorData addSensorBData(SensorAData sensorAData)
{
return new SensorData
{
SensorAData = sensorAData,
SensorBData = readSensorBData()
};
}
static SensorAData readSensorAData()
{
Console.WriteLine("Reading from Sensor A");
Thread.Sleep(1000); // Simulate reading sensor A data taking some time.
int value = Interlocked.Increment(ref sensorValue);
Console.WriteLine("Read Sensor A value = " + value);
return new SensorAData {Data = value};
}
static SensorBData readSensorBData()
{
Console.WriteLine("Reading from Sensor B");
Thread.Sleep(100); // Simulate reading sensor B data being much quicker.
int value = Interlocked.Increment(ref sensorValue);
Console.WriteLine("Read Sensor B value = " + value);
return new SensorBData {Data = value};
}
static void process(SensorData value)
{
Console.WriteLine("Processing sensor data: " + value);
Thread.Sleep(1000); // Simulate slow processing of combined sensor values.
}
static ExecutionDataflowBlockOptions dataflowOptions()
{
return new ExecutionDataflowBlockOptions
{
MaxDegreeOfParallelism = 1,
BoundedCapacity = 1
};
}
static int sensorValue;
}
}
Related
I had tried background worker, thread and task but the procedure are so long to get a solution according to my question. suppose I use a task, I can't add any textboxes in ABC function which is called in button_click. When I use textbox inside button click function on the place of ABC function it's worked. But this process is not fulfil my requirement.
If I called this function directly into buttonclick event it's hang my UI.
Here is the example code of ABC function.
public void ABC()
{
While(true)
{
richTextBox1.AppendText("Hello");
richTextBox2.AppendText("Tesing");
}
}
If anyone have an idea to achieve this, please tell me.
I am trying this because my sensor gives me data on continuous basis and I used these values to draw a graph, so I tried to run that in background to update my graphs on continuous basis.
Wait let me explain I have sensor which sends there data on API every 5 minutes in JSON format. Now my code fetching data from API and display a graph after button click. When I click on button my graph is updated with new data but I want to try to convert this manual process into automatic process. I mean when I click on button my function should have run continuously and update data on graph without hanging UI because I have to perform another operations on graph. Here is my function with original code:
public void EnvlopGraph()
{
//while (true)
//{
graph = new Graph();
//frmdeviceconfig deviceinfo = new frmdeviceconfig(this);
grpboxselectaxis.Enabled = false;
chkboxxaxis.Checked = true;
chkboxyaxis.Checked = false;
chkboxzaxis.Checked = false;
frm.GetSensorId();
frm.GetOverall();
chartview1.Visible = true;
grpboxselectaxis.Visible = true;
double[] XAxisRange;
envlpxfft.Clear();
envlpyfft.Clear();
envlpxtime.Clear();
envlpytime.Clear();
var client = new RestClient(frm.ClientURL);
var request = new RestRequest($"api/traces", Method.GET);
try
{
IRestResponse response = client.Execute(request);
JsonDeserializer deserial = new JsonDeserializer();
dynamic obj = deserial.Deserialize<dynamic>(response);
var objindexcount = obj.Count;
var TracesData = obj[objindexcount - 1]; //this is applicable on current Bearing data only
//var TracesData = obj[objindexcount - 2]; // for current Tri_Acc Data (x,y,z)
var SerialNumber = TracesData["serialNumber"];
if (SerialNumber == frmdeviceconfig.stpointsensor)
{
var RouteTime = TracesData["routeTime"];
var SampleTime = TracesData["sampleTime"];
var Name = TracesData["name"];
var SignalPath = TracesData["signalPath"];
var Range = TracesData["range"];
string SampleRate = TracesData["sampleRate"];
string[] SR = SampleRate.Split('S', 'a', 'm', 'p', 'l', 'e', 'R', 'a', 't', 'e', '_');
double SampleRateVal = Convert.ToDouble(SR[11]);
string TraceLength = TracesData["traceLength"];
var XAxisTrace = TracesData["xAxisTrace"];
string[] TL = TraceLength.Split('T', 'r', 'a', 'c', 'e', 'L', 'e', 'n', 'g', 't', 'h', '_');
double TraceLengthVal = Convert.ToDouble(TL[12]);
double xtimerange = Convert.ToDouble((TraceLengthVal / TraceLengthVal) / TraceLengthVal);
if (chartview1.Controls.Count > 0)
{
chartview1.Controls.Clear();
chartview1.Controls.Remove(graph);
}
if (XAxisTrace != null)
{
var XAdcRangePercent = XAxisTrace["adcRangePercent"];
var XClipped = XAxisTrace["clipped"];
var XRangeApplied = XAxisTrace["rangeApplied"];
var XGravityRemoved = XAxisTrace["gravityRemoved"];
var XTimeWave = XAxisTrace["timewave"];
var xaxisdatalength = XTimeWave.Count;
XData = new double[xaxisdatalength];
XAxisRange = new double[xaxisdatalength];
for (int i = 0; i < xaxisdatalength; i++)
{
XData[i] = XTimeWave[i];
XAxisRange[i] = i * xtimerange;
}
//Add graph
envlpxtime.Add(XAxisRange);
envlpytime.Add(XData);
if (Display == 1)
{
if (GraphType != "FFT")
{
graph.DrawLineGraph(envlpxtime, envlpytime);
}
else
{
//fft graph for x axis
FFTConversion fft = new FFTConversion();
if (XData == null)
{
MessageBox.Show("TimeData is not available");
}
else
{
double[] fftdata = fft.ConvertTimeDataToFFT(XData);
chartview1.Visible = true;
string[] linekhz = Name.Split('B', '_', 'X', 'R', 'G', 'L', 'H');
double lines = Convert.ToDouble(linekhz[8]);
double hz = Convert.ToDouble(linekhz[10]);
double fftXrange = hz / lines;
for (int k = 0; k < fftdata.Length; k++)
{
XAxisRange[k] = k * fftXrange;
}
envlpxfft.Add(XAxisRange);
envlpyfft.Add(fftdata);
graph.DrawLineGraph(envlpxfft, envlpyfft);
}
}
graph.Dock = DockStyle.Fill;
chartview1.Controls.Add(graph);
}
}
else
{
}
}
}
catch (Exception ex)
{
}
}
I believe that there is a better way, but sometimes you can't just change what is already thereā¦ In those cases, I use the Producer/Consumer Pattern for problems that are similar to yours.
1) Create the ProducerConsumer class.
public class ProducerConsumer<T>
{
private readonly BlockingCollection<T> mCollection = new BlockingCollection<T>(100);
private readonly Action<T> mConsumer;
private readonly object SYNC_LOCK = new object();
public ProducerConsumer(Action<T> consumer) {
mConsumer = consumer;
}
public void Produce(T obj) {
mCollection.Add(obj);
}
// If using multiple producers
public void ThreadSafeProduce(T obj) {
lock (SYNC_LOCK ) {
Produce(obj);
}
}
private void Loop() {
while (true) {
var obj = mCollection.Take();
if (obj == null) return;
mConsumer(obj);
}
}
public void Start() {
Task.Run(() => Loop());
}
}
2) Example of how to use it
public partial class Form1 : Form
{
private ProducerConsumer<string> mProducerConsumer;
public Form1()
{
InitializeComponent();
mProducerConsumer = new ProducerConsumer<string>(Consumer);
mProducerConsumer.Start();
FakeSensorReadings();
}
private void FakeSensorReadings()
{
Task.Run(() =>
{
while (true)
{
mProducerConsumer.Produce("Teste string\r\n");
Thread.Sleep(100);
}
});
}
private void Consumer(string str) {
// Run on Main Thread
this.Invoke(new MethodInvoker(() =>
{
textBox1.Text += str;
}));
}
}
HINT
You can use any object type that you want, like int, float, struct, custom objects, etc.
So you have a Sensor class that produces data, and you have another class that processes (= consumes) the produced data. Sometimes the Producer produces more data than the Consumer can process, other times the Consumer has enough time to process the produced data.
A problem like this screams for the Producer-Consumer pattern: a Producer produces data that a Consumer processes in a different tempo: sometimes the Producer is faster, sometimes the Consumer is faster.
For this, Microsoft came up with the Dataflow Task Parallel Library (TPL). You can download the code using the nuget package manager.
Let's assume your Sensor produces a stream of SensorData, sometimes faster, sometimes slower.
Your Consumer process needs to take this Sensor data and process it. While this data is being processed, the Sensor might produce new SensorData. This SensorData needs to be buffered.
This buffering is done using the TPL BufferBlock. Whenever your Sensor produces some SensorData it Adds the data to the BufferBlock, and starts Producing the next SensorData.
Meanwhile, the Consumer does nothing but wait until there is some SensorData in the BufferBlock. As soon as it arrives, it removes it from the BufferBlock and processes the SensorData. When finished processing, it waits until there is SensorData again.
So if the Producer is (temporarily) faster than the Consumer, then there will be several SensorData in the BufferBlock. If the Consumer is faster, the BufferBlock will regularly be empty.
This process stops when the Sensor knows that there is no more data. It notifies the BufferBlock. When all SensorData in the BufferBlock are processed, the Consumer is notified that no more data is to be expected.
class SensorData {...};
class Sensor
{
// send Produced SensorData to this ITargetBlock:
public ITargetBlock<SensorData> DataBuffer {get; set;}
private SensorData ReadSensor() {...}
public async Task ProduceDataAsync(CancellationToken token)
{
// TODO: check if this.TargetBuffer and token not null
while (!token.IsCancellationRequested)
{
SensorData sensorData = this.ReadSensor();
await this.TargetBuffer.SendAsync(token);
}
// if here: cancellation requested. Stop producing SensorData
// notify that no data will be produced anymore
this.TargetBuffer.Complete();
}
}
The Consumer:
class Consumer
{
// the Consumer will listen for data here:
public ISourceBlock<SensorData> DataBuffer {get; set;}
private void ProcessSensorData(SensorData sensorData) {...}
public async Task ConsumeDataAsync()
{
// TODO: check if DataBuffer
// as long as there is SensorData expected: wait for it and process
while (await this.DataBuffer.OutputAvailableAsync())
{
// There is some data; fetch it and process it
SensorData sensorData = await this.DataBuffer.ReceiveAsync();
this.ProcessSensorData(sensorData);
}
// if here: no more SensorData expected
}
}
Putting it all together
BufferBlock<SensorData> buffer = new BufferBlock<SensorData>();
Sensor sensor = new Sensor
{
DataBuffer = buffer,
}
Consumer consumer = new Consumer
{
DataBuffer = buffer,
}
Now everything is ready to be started. For instance when pressing a button:
async void OnButtonStart_Clicked(object sender, ...)
{
this.buttonStart.Enabled = false; // to prevent starting again
this.buttonStop.Enabled = true; // so we can stop the process
await this.StartProcessingAsync();
this.buttonStop.Enabled = false;
this.buttonStart.Enabled = true;
}
private CancellationTokenSource CancellationTokenSource {get; set;}
async Task StartProcessingAsync()
{
using (this.CancellationTokenSource = new CancellationTokenSource())
{
CancellationToken token = this.CancellationTokenSource.Token;
// start Producing and Consuming:
Task producerTask = sensor.ProduceDataAsync(token);
Task consumerTask = consumer.ConsumeDataAsync();
// continue producing and consuming until both tasks are finished
await Taks.WhenAll(new Task[] {producerTask, consumerTask});
}
}
To stop the process:
async void StopButton_ClickedAsync(object sender, ...)
{
this.NotifyProducerBeingCancelled(); // notify the operator
// Start the cancellation process
this.CancellationTokenSource.Cancel();
}
Cancelling the CancellationTokenSource will notify all Tokens from this Source. The Producer has such a token. It will check regularly whether cancellation is requested. If so, it will stop producing SensorData and notify the Buffer that no data is to be expected anymore. The method will return.
Meanwhile the Consumer is happily consuming SensorData. If all Produced SensorData has been processed, it sees that no more data is to be expected. The method will return.
Method StartProcessingAsync was waiting until both tasks ProduceDataAsync and ConsumeDataAsync are finished. When both are finished it will Enable and Disable the buttons again and return.
If the operator clicks the Stop Button several times before the Producer and Consumer are stopped, then only the CancellationTokenSource is cancelled again, which won't do anything.
Note that this whole thing is done by one thread. Or to be more accurate: only one context is used, this means that it is as if only one thread does everything. Therefore there is no need for you to protect using mutexes or semaphores. No need for InvokeRequired in method ProcessSensorData, because the thread has the UI context, and thus can update UI elements.
If the update of the UI elements was only as an example in your question, and you don't need to update UI elements at all, but for instance only save data in a file, consider to add ConfigureAwait(false) in your await statements
The Producer is
I need to process data from a producer in FIFO fashion with the ability to abort processing if the same producer produces a new bit of data.
So I implemented an abortable FIFO queue based on Stephen Cleary's AsyncCollection (called AsyncCollectionAbortableFifoQueuein my sample) and one on TPL's BufferBlock (BufferBlockAbortableAsyncFifoQueue in my sample). Here's the implementation based on AsyncCollection
public class AsyncCollectionAbortableFifoQueue<T> : IExecutableAsyncFifoQueue<T>
{
private AsyncCollection<AsyncWorkItem<T>> taskQueue = new AsyncCollection<AsyncWorkItem<T>>();
private readonly CancellationToken stopProcessingToken;
public AsyncCollectionAbortableFifoQueue(CancellationToken cancelToken)
{
stopProcessingToken = cancelToken;
_ = processQueuedItems();
}
public Task<T> EnqueueTask(Func<Task<T>> action, CancellationToken? cancelToken)
{
var tcs = new TaskCompletionSource<T>();
var item = new AsyncWorkItem<T>(tcs, action, cancelToken);
taskQueue.Add(item);
return tcs.Task;
}
protected virtual async Task processQueuedItems()
{
while (!stopProcessingToken.IsCancellationRequested)
{
try
{
var item = await taskQueue.TakeAsync(stopProcessingToken).ConfigureAwait(false);
if (item.CancelToken.HasValue && item.CancelToken.Value.IsCancellationRequested)
item.TaskSource.SetCanceled();
else
{
try
{
T result = await item.Action().ConfigureAwait(false);
item.TaskSource.SetResult(result); // Indicate completion
}
catch (Exception ex)
{
if (ex is OperationCanceledException && ((OperationCanceledException)ex).CancellationToken == item.CancelToken)
item.TaskSource.SetCanceled();
item.TaskSource.SetException(ex);
}
}
}
catch (Exception) { }
}
}
}
public interface IExecutableAsyncFifoQueue<T>
{
Task<T> EnqueueTask(Func<Task<T>> action, CancellationToken? cancelToken);
}
processQueuedItems is the task that dequeues AsyncWorkItem's from the queue, and executes them unless cancellation has been requested.
The asynchronous action to execute gets wrapped into an AsyncWorkItem which looks like this
internal class AsyncWorkItem<T>
{
public readonly TaskCompletionSource<T> TaskSource;
public readonly Func<Task<T>> Action;
public readonly CancellationToken? CancelToken;
public AsyncWorkItem(TaskCompletionSource<T> taskSource, Func<Task<T>> action, CancellationToken? cancelToken)
{
TaskSource = taskSource;
Action = action;
CancelToken = cancelToken;
}
}
Then there's a task looking and dequeueing items for processing and either processing them, or aborting if the CancellationToken has been triggered.
That all works just fine - data gets processed, and if a new piece of data is received, processing of the old is aborted. My problem now stems from these Queues leaking massive amounts of memory if I crank up the usage (producer producing a lot more than the consumer processes). Given it's abortable, the data that is not processed, should be discarded and eventually disappear from memory.
So let's look at how I'm using these queues. I have a 1:1 match of producer and consumer. Every consumer handles data of a single producer. Whenever I get a new data item, and it doesn't match the previous one, I catch the queue for the given producer (User.UserId) or create a new one (the 'executor' in the code snippet). Then I have a ConcurrentDictionary that holds a CancellationTokenSource per producer/consumer combo. If there's a previous CancellationTokenSource, I call Cancel on it and Dispose it 20 seconds later (immediate disposal would cause exceptions in the queue). I then enqueue processing of the new data. The queue returns me a task that I can await so I know when processing of the data is complete, and I then return the result.
Here's that in code
internal class SimpleLeakyConsumer
{
private ConcurrentDictionary<string, IExecutableAsyncFifoQueue<bool>> groupStateChangeExecutors = new ConcurrentDictionary<string, IExecutableAsyncFifoQueue<bool>>();
private readonly ConcurrentDictionary<string, CancellationTokenSource> userStateChangeAborters = new ConcurrentDictionary<string, CancellationTokenSource>();
protected CancellationTokenSource serverShutDownSource;
private readonly int operationDuration = 1000;
internal SimpleLeakyConsumer(CancellationTokenSource serverShutDownSource, int operationDuration)
{
this.serverShutDownSource = serverShutDownSource;
this.operationDuration = operationDuration * 1000; // convert from seconds to milliseconds
}
internal async Task<bool> ProcessStateChange(string userId)
{
var executor = groupStateChangeExecutors.GetOrAdd(userId, new AsyncCollectionAbortableFifoQueue<bool>(serverShutDownSource.Token));
CancellationTokenSource oldSource = null;
using (var cancelSource = userStateChangeAborters.AddOrUpdate(userId, new CancellationTokenSource(), (key, existingValue) =>
{
oldSource = existingValue;
return new CancellationTokenSource();
}))
{
if (oldSource != null && !oldSource.IsCancellationRequested)
{
oldSource.Cancel();
_ = delayedDispose(oldSource);
}
try
{
var executionTask = executor.EnqueueTask(async () => { await Task.Delay(operationDuration, cancelSource.Token).ConfigureAwait(false); return true; }, cancelSource.Token);
var result = await executionTask.ConfigureAwait(false);
userStateChangeAborters.TryRemove(userId, out var aborter);
return result;
}
catch (Exception e)
{
if (e is TaskCanceledException || e is OperationCanceledException)
return true;
else
{
userStateChangeAborters.TryRemove(userId, out var aborter);
return false;
}
}
}
}
private async Task delayedDispose(CancellationTokenSource src)
{
try
{
await Task.Delay(20 * 1000).ConfigureAwait(false);
}
finally
{
try
{
src.Dispose();
}
catch (ObjectDisposedException) { }
}
}
}
In this sample implementation, all that is being done is wait, then return true.
To test this mechanism, I wrote the following Data producer class:
internal class SimpleProducer
{
//variables defining the test
readonly int nbOfusers = 10;
readonly int minimumDelayBetweenTest = 1; // seconds
readonly int maximumDelayBetweenTests = 6; // seconds
readonly int operationDuration = 3; // number of seconds an operation takes in the tester
private readonly Random rand;
private List<User> users;
private readonly SimpleLeakyConsumer consumer;
protected CancellationTokenSource serverShutDownSource, testAbortSource;
private CancellationToken internalToken = CancellationToken.None;
internal SimpleProducer()
{
rand = new Random();
testAbortSource = new CancellationTokenSource();
serverShutDownSource = new CancellationTokenSource();
generateTestObjects(nbOfusers, 0, false);
consumer = new SimpleLeakyConsumer(serverShutDownSource, operationDuration);
}
internal void StartTests()
{
if (internalToken == CancellationToken.None || internalToken.IsCancellationRequested)
{
internalToken = testAbortSource.Token;
foreach (var user in users)
_ = setNewUserPresence(internalToken, user);
}
}
internal void StopTests()
{
testAbortSource.Cancel();
try
{
testAbortSource.Dispose();
}
catch (ObjectDisposedException) { }
testAbortSource = new CancellationTokenSource();
}
internal void Shutdown()
{
serverShutDownSource.Cancel();
}
private async Task setNewUserPresence(CancellationToken token, User user)
{
while (!token.IsCancellationRequested)
{
var nextInterval = rand.Next(minimumDelayBetweenTest, maximumDelayBetweenTests);
try
{
await Task.Delay(nextInterval * 1000, testAbortSource.Token).ConfigureAwait(false);
}
catch (TaskCanceledException)
{
break;
}
//now randomly generate a new state and submit it to the tester class
UserState? status;
var nbStates = Enum.GetValues(typeof(UserState)).Length;
if (user.CurrentStatus == null)
{
var newInt = rand.Next(nbStates);
status = (UserState)newInt;
}
else
{
do
{
var newInt = rand.Next(nbStates);
status = (UserState)newInt;
}
while (status == user.CurrentStatus);
}
_ = sendUserStatus(user, status.Value);
}
}
private async Task sendUserStatus(User user, UserState status)
{
await consumer.ProcessStateChange(user.UserId).ConfigureAwait(false);
}
private void generateTestObjects(int nbUsers, int nbTeams, bool addAllUsersToTeams = false)
{
users = new List<User>();
for (int i = 0; i < nbUsers; i++)
{
var usr = new User
{
UserId = $"User_{i}",
Groups = new List<Team>()
};
users.Add(usr);
}
}
}
It uses the variables at the beginning of the class to control the test. You can define the number of users (nbOfusers - every user is a producer that produces new data), the minimum (minimumDelayBetweenTest) and maximum (maximumDelayBetweenTests) delay between a user producing the next data and how long it takes the consumer to process the data (operationDuration).
StartTests starts the actual test, and StopTests stops the tests again.
I'm calling these as follows
static void Main(string[] args)
{
var tester = new SimpleProducer();
Console.WriteLine("Test successfully started, type exit to stop");
string str;
do
{
str = Console.ReadLine();
if (str == "start")
tester.StartTests();
else if (str == "stop")
tester.StopTests();
}
while (str != "exit");
tester.Shutdown();
}
So, if I run my tester and type 'start', the Producer class starts producing states that are consumed by Consumer. And memory usage starts to grow and grow and grow. The sample is configured to the extreme, the real-life scenario I'm dealing with is less intensive, but one action of the producer could trigger multiple actions on the consumer side which also have to be executed in the same asynchronous abortable fifo fashion - so worst case, one set of data produced triggers an action for ~10 consumers (that last part I stripped out for brevity).
When I'm having a 100 producers, and each producer produces a new data item every 1-6 seconds (randomly, also the data produces is random). Consuming the data takes 3 seconds.. so there's plenty of cases where there's a new set of data before the old one has been properly processed.
Looking at two consecutive memory dumps, it's obvious where the memory usage is coming from.. it's all fragments that have to do with the queue. Given that I'm disposing every TaskCancellationSource and not keeping any references to the produced data (and the AsyncWorkItem they're put into), I'm at a loss to explain why this keeps eating up my memory and I'm hoping somebody else can show me the errors of my way. You can also abort testing by typing 'stop'.. you'll see that no longer is memory being eaten, but even if you pause and trigger GC, memory is not being freed either.
The source code of the project in runnable form is on Github. After starting it, you have to type start (plus enter) in the console to tell the producer to start producing data. And you can stop producing data by typing stop (plus enter)
Your code has so many issues making it impossible to find a leak through debugging. But here are several things that already are an issue and should be fixed first:
Looks like getQueue creates a new queue for the same user each time processUseStateUpdateAsync gets called and does not reuse existing queues:
var executor = groupStateChangeExecutors.GetOrAdd(user.UserId, getQueue());
CancellationTokenSource is leaking on each call of the code below, as new value created each time the method AddOrUpdate is called, it should not be passed there that way:
userStateChangeAborters.AddOrUpdate(user.UserId, new CancellationTokenSource(), (key, existingValue
Also code below should use the same cts as you pass as new cts, if dictionary has no value for specific user.UserId:
return new CancellationTokenSource();
Also there is a potential leak of cancelSource variable as it gets bound to a delegate which can live for a time longer than you want, it's better to pass concrete CancellationToken there:
executor.EnqueueTask(() => processUserStateUpdateAsync(user, state, previousState,
cancelSource.Token));
By some reason you do not dispose aborter here and in one more place:
userStateChangeAborters.TryRemove(user.UserId, out var aborter);
Creation of Channel can have potential leaks:
taskQueue = Channel.CreateBounded<AsyncWorkItem<T>>(new BoundedChannelOptions(1)
You picked option FullMode = BoundedChannelFullMode.DropOldest which should remove oldest values if there are any, so I assume that that stops queued items from processing as they would not be read. It's a hypotheses, but I assume that if an old item is removed without being handled, then processUserStateUpdateAsync won't get called and all resources won't be freed.
You can start with these found issues and it should be easier to find the real cause after that.
I would like to import a CSV file in its own thread. While the file is imported and processed I would like to delay/stop the main thread until the processing have ended. See the code below:
// Read from CSV file in a seperate thread
new Thread(() =>
{
Thread.CurrentThread.IsBackground = true;
reader = new CSVReader(myFile);
reader.DataReader();
// Get temperature and time data from CSV file
// and copy the data into each List<String>
Temperature = new List<string>(reader.GetTemperature());
Time = new List<string>(reader.GetTime());
}).Start();
// Bind data to GridView
dtgCsvData.ItemsSource = Time.Zip(Temperature, (t, c) => new { Time = t, Temperature = c });
When the application is running a error occur, since the two Lists are null.
How do I achieve this?
This was created using linqpad... You can either use a task or async keyword as follows.
void Main()
{
Task<TimeAndTemp> timeAndTempTask = GetTimeAndTemp();
timeAndTempTask.ContinueWith (_ =>
{
timeAndTempTask.Result.Time.Dump();
timeAndTempTask.Result.Temperature.Dump();
});
}
Task<TimeAndTemp> GetTimeAndTemp()
{
var tcs = new TaskCompletionSource<TimeAndTemp>();
new Timer (_ => tcs.SetResult (new TimeAndTemp())).Change (3000, -1);
return tcs.Task;
}
public class TimeAndTemp
{
public DateTime Time = DateTime.Now;
public int Temperature = 32;
}
async version using async await keywords.
async void Main()
{
TimeAndTemp tt = await GetTimeAndTemp();
tt.Time.Dump();
tt.Temperature.Dump();
}
async Task<TimeAndTemp> GetTimeAndTemp()
{
return new TimeAndTemp();
}
public class TimeAndTemp
{
public DateTime Time = DateTime.Now;
public int Temperature = 32;
}
You probably don't really want to stop the main thread. If this is a GUI application, you still want the main UI thread to respond to Windows messages and the like. What you want is to run that piece of code after the data has been read. Why not have it be called by the worked thread after it reads the data?
I have a compute intensive method Calculate that may run for a few seconds, requests come from multiple threads.
Only one Calculate should be executing, a subsequent request should be queued until the initial request completes. If there is already a request queued then the the subsequent request can be discarded (as the queued request will be sufficient)
There seems to be lots of potential solutions but I just need the simplest.
UPDATE: Here's my rudimentaryattempt:
private int _queueStatus;
private readonly object _queueStatusSync = new Object();
public void Calculate()
{
lock(_queueStatusSync)
{
if(_queueStatus == 2) return;
_queueStatus++;
if(_queueStatus == 2) return;
}
for(;;)
{
CalculateImpl();
lock(_queueStatusSync)
if(--_queueStatus == 0) return;
}
}
private void CalculateImpl()
{
// long running process will take a few seconds...
}
The simplest, cleanest solution IMO is using TPL Dataflow (as always) with a BufferBlock acting as the queue. BufferBlock is thread-safe, supports async-await, and more important, has TryReceiveAll to get all the items at once. It also has OutputAvailableAsync so you can wait asynchronously for items to be posted to the buffer. When multiple requests are posted you simply take the last and forget about the rest:
var buffer = new BufferBlock<Request>();
var task = Task.Run(async () =>
{
while (await buffer.OutputAvailableAsync())
{
IList<Request> requests;
buffer.TryReceiveAll(out requests);
Calculate(requests.Last());
}
});
Usage:
buffer.Post(new Request());
buffer.Post(new Request());
Edit: If you don't have any input or output for the Calculate method you can simply use a boolean to act as a switch. If it's true you can turn it off and calculate, if it became true again while Calculate was running then calculate again:
public bool _shouldCalculate;
public void Producer()
{
_shouldCalculate = true;
}
public async Task Consumer()
{
while (true)
{
if (!_shouldCalculate)
{
await Task.Delay(1000);
}
else
{
_shouldCalculate = false;
Calculate();
}
}
}
A BlockingCollection that only takes 1 at a time
The trick is to skip if there are any items in the collection
I would go with the answer from I3aron +1
This is (maybe) a BlockingCollection solution
public static void BC_AddTakeCompleteAdding()
{
using (BlockingCollection<int> bc = new BlockingCollection<int>(1))
{
// Spin up a Task to populate the BlockingCollection
using (Task t1 = Task.Factory.StartNew(() =>
{
for (int i = 0; i < 100; i++)
{
if (bc.TryAdd(i))
{
Debug.WriteLine(" add " + i.ToString());
}
else
{
Debug.WriteLine(" skip " + i.ToString());
}
Thread.Sleep(30);
}
bc.CompleteAdding();
}))
{
// Spin up a Task to consume the BlockingCollection
using (Task t2 = Task.Factory.StartNew(() =>
{
try
{
// Consume consume the BlockingCollection
while (true)
{
Debug.WriteLine("take " + bc.Take());
Thread.Sleep(100);
}
}
catch (InvalidOperationException)
{
// An InvalidOperationException means that Take() was called on a completed collection
Console.WriteLine("That's All!");
}
}))
Task.WaitAll(t1, t2);
}
}
}
It sounds like a classic producer-consumer. I'd recommend looking into BlockingCollection<T>. It is part of the System.Collection.Concurrent namespace. On top of that you can implement your queuing logic.
You may supply to a BlockingCollection any internal structure to hold its data, such as a ConcurrentBag<T>, ConcurrentQueue<T> etc. The latter is the default structure used.
I have 1 thread streaming data and a 2nd (the threadpool) processing the data. The data processing takes around 100ms so I use to second thread so not to hold up the 1st thread.
While the 2nd thread is processing the data the 1st thread adds the data to a dictionary cache then when the 2nd thread is finished it processes the cached values.
My questions is this how should be doing producer /consumer code in C#?
public delegate void OnValue(ulong value);
public class Runner
{
public event OnValue OnValueEvent;
private readonly IDictionary<string, ulong> _cache = new Dictionary<string, ulong>(StringComparer.InvariantCultureIgnoreCase);
private readonly AutoResetEvent _cachePublisherWaitHandle = new AutoResetEvent(true);
public void Start()
{
for (ulong i = 0; i < 500; i++)
{
DataStreamHandler(i.ToString(), i);
}
}
private void DataStreamHandler(string id, ulong value)
{
_cache[id] = value;
if (_cachePublisherWaitHandle.WaitOne(1))
{
IList<ulong> tempValues = new List<ulong>(_cache.Values);
_cache.Clear();
_cachePublisherWaitHandle.Reset();
ThreadPool.UnsafeQueueUserWorkItem(delegate
{
try
{
foreach (ulong value1 in tempValues)
if (OnValueEvent != null)
OnValueEvent(value1);
}
finally
{
_cachePublisherWaitHandle.Set();
}
}, null);
}
else
{
Console.WriteLine(string.Format("Buffered value: {0}.", value));
}
}
}
class Program
{
static void Main(string[] args)
{
Stopwatch sw = Stopwatch.StartNew();
Runner r = new Runner();
r.OnValueEvent += delegate(ulong x)
{
Console.WriteLine(string.Format("Processed value: {0}.", x));
Thread.Sleep(100);
if(x == 499)
{
sw.Stop();
Console.WriteLine(string.Format("Time: {0}.", sw.ElapsedMilliseconds));
}
};
r.Start();
Console.WriteLine("Done");
Console.ReadLine();
}
}
The best practice for setting up the producer-consumer pattern is to use the BlockingCollection class which is available in .NET 4.0 or as a separate download of the Reactive Extensions framework. The idea is that the producers will enqueue using the Add method and the consumers will dequeue using the Take method which blocks if the queue is empty. Like SwDevMan81 pointed out the Albahari site has a really good writeup on how to make it work correctly if you want to go the manual route.
There is a good article on MSDN about Synchronizing the Producer and Consumer. There is also a good example on the Albahari site.