WriteableBitmap in a background worker - multithreading

I currently have a loop something like this:
foreach(var item in theList)
{
//create writeablebitmap from item.GetPicture() which returns a stream.
//do stuff with it
//save it to isolated storage
}
This is a very long-running process because there are tons of images. I tried putting it in a background worker but you cannot create WriteableBitmaps there (they have to be on the UI thread). The problem is that the interface is totally unresponsive.
How can I make it process keypresses/the UI once every loop, just to make sure it can respond to the user doing things?

BackgroundWorker backgroundWorker = new BackgroundWorker();
backgroundWorker.DoWork += (s, e) =>
{
foreach(var item in theList)
{
var item _ = item;
Deployment.Dispatcher.BeginInvoke(() =>
{
// do your stuff on UI thread with item_
});
Thread.Sleep(milliseconds); //experiment with delay to find balance between UI responsiveness and performance
}
};
backgroundWorker.RunWorkAsync();

Related

UWP update UI control from a task running in a different thread

UI has a label named lblMyLabel.
It’s bound to a view model MyViewModel.
A task is running on a different thread which pulls message from a service.( for example)
When a this task receives a specific value it should update the UI label with it.
I always get a error that it was Marshalled in a different thread .
When you want to update UI,you should run on the UI's dispatcher thread.You can call Dispatcher.RunAsync to back onto the UI's dispatcher thread.
private void Button_Click(object sender, RoutedEventArgs e)
{
//Start a Task
Task t = Task.Factory.StartNew(() => {
int i = 0;
for (i = 0; i <= 1000; i++)
{
}
//Back onto the UI thread
var a = Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
{
//Update UI
MyTextBlock.Text = "Update";
});
});
}

Winforms updates with high performance

Let me setup this question with some background information, we have a long running process which will be generating data in a Windows Form. So, obviously some form of multi-threading is going to be needed to keep the form responsive. But, we also have the requirement that the form updates as many times per second while still remaining responsive.
Here is a simple test example using background worker thread:
void bw_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
int reportValue = (int)e.UserState;
label1.Text = reportValue;
//We can put this.Refresh() here to force repaint which gives us high repaints but we lose
//all other responsiveness with the control
}
void bw_DoWork(object sender, DoWorkEventArgs e)
{
for (int x = 0; x < 100000; x++)
{
//We could put Thread.Sleep here but we won't get highest performance updates
bw.ReportProgress(0, x);
}
}
Please see the comments in the code. Also, please don't question why I want this. The question is simple, how do we achieve the highest fidelity (most repaints) in updating the form while maintaining responsiveness? Forcing the repaint does give us updates but we don't process windows messages.
I have also try placing DoEvents but that produces stack overflow. What I need is some way to say, "process any windows messages if you haven't lately". I can see also that maybe a slightly different pattern is needed to achieve this.
It seems we need to handle a few issues:
Updating the Form through the non UI thread. There are quite a few solution to this problem such as invoke, synchronization context, background worker pattern.
The second problem is flooding the Form with too many updates which blocks the message processing and this is the issue around which my question really concerns. In most examples, this is handles trivially by slowing down the requests with an arbitrary wait or only updating every X%. Neither of these solutions are approriate for real-world applications nor do they meet the maximum update while responsive criteria.
Some of my initial ideas on how to handle this:
Queue the items in the background worker and then dispatch them in a UI thread. This will ensure every item is painted but will result in lag which we don't want.
Perhaps use TPL
Perhaps use a timer in the UI thread to specify a refresh value. In this way, we can grab the data at the fastest rate that we can process. It will require accessing/sharing data across threads.
Update, I've updated to use a Timer to read a shared variable with the Background worker thread updates. Now for some reason, this method produces a good form response and also allows the background worker to update about 1,000x as fast. But, interestingly it only 1 millisecond accurate.
So we should be able to change the pattern to read the current time and call the updates from the bw thread without the need for the timer.
Here is the new pattern:
//Timer setup
{
RefreshTimer.SynchronizingObject = this;
RefreshTimer.Elapsed += RefreshTimer_Elapsed;
RefreshTimer.AutoReset = true;
RefreshTimer.Start();
}
void bw_DoWork(object sender, DoWorkEventArgs e)
{
for (int x = 0; x < 1000000000; x++)
{
//bw.ReportProgress(0, x);
//mUiContext.Post(UpdateLabel, x);
SharedX = x;
}
}
void RefreshTimer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
label1.Text = SharedX.ToString();
}
Update And here we have the new solution that doesn't require the timer and doesn't block the thread! We achieve a high performance in calculations and fidelity on the updates with this pattern. Unfortunately, ticks TickCount is only 1 MS accurate, however we can run a batch of X updates per MS to get faster then 1 MS timing.
void bw_DoWork(object sender, DoWorkEventArgs e)
{
long lastTickCount = Environment.TickCount;
for (int x = 0; x < 1000000000; x++)
{
if (Environment.TickCount - lastTickCount > 1)
{
bw.ReportProgress(0, x);
lastTickCount = Environment.TickCount;
}
}
}
There is little point in trying to report progress any faster than the user can keep track of it.
If your background thread is posting messages faster than the GUI can process them, (and you have all the symtoms of this - poor GUI resonse to user input, DoEvents runaway recursion), you have to throttle the progress updates somehow.
A common approach is to update the GUI using a main-thread form timer at a rate sufficiently small that the user sees an acceptable progress readout. You may need a mutex or critical section to protect shared data, though that amy not be necessary if the progress value to be monitored is an int/uint.
An alternative is to strangle the thread by forcing it to block on an event or semaphore until the GUI is idle.
The UI thread should not be held for more than 50ms by a CPU-bound operation taking place on it ("The 50ms Rule"). Usually, the UI work items are executed upon events, triggered by user input, completion of an IO-bound operation or a CPU-bound operation offloaded to a background thread.
However, there are some rare cases when the work needs to be done on the UI thread. For example, you may need to poll a UI control for changes, because the control doesn't expose proper onchange-style event. Particularly, this applies to WebBrowser control (DOM Mutation Observers are only being introduced, and IHTMLChangeSink doesn't always work reliably, in my experience).
Here is how it can be done efficiently, without blocking the UI thread message queue. A few key things was used here to make this happen:
The UI work tasks yields (via Application.Idle) to process any pending messages
GetQueueStatus is used to decide on whether to yield or not
Task.Delay is used to throttle the loop, similar to a timer event. This step is optional, if the polling needs to be as precise as possible.
async/await provide pseudo-synchronous linear code flow.
using System;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace WinForms_21643584
{
public partial class MainForm : Form
{
EventHandler ContentChanged = delegate { };
public MainForm()
{
InitializeComponent();
this.Load += MainForm_Load;
}
// Update UI Task
async Task DoUiWorkAsync(CancellationToken token)
{
try
{
var startTick = Environment.TickCount;
var editorText = this.webBrowser.Document.Body.InnerText;
while (true)
{
// observe cancellation
token.ThrowIfCancellationRequested();
// throttle (optional)
await Task.Delay(50);
// yield to keep the UI responsive
await ApplicationExt.IdleYield();
// poll the content for changes
var newEditorText = this.webBrowser.Document.Body.InnerText;
if (newEditorText != editorText)
{
editorText = newEditorText;
this.status.Text = "Changed on " + (Environment.TickCount - startTick) + "ms";
this.ContentChanged(this, EventArgs.Empty);
}
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
async void MainForm_Load(object sender, EventArgs e)
{
// navigate the WebBrowser
var documentTcs = new TaskCompletionSource<bool>();
this.webBrowser.DocumentCompleted += (sIgnore, eIgnore) => documentTcs.TrySetResult(true);
this.webBrowser.DocumentText = "<div style='width: 100%; height: 100%' contentEditable='true'></div>";
await documentTcs.Task;
// cancel updates in 10 s
var cts = new CancellationTokenSource(20000);
// start the UI update
var task = DoUiWorkAsync(cts.Token);
}
}
// Yield via Application.Idle
public static class ApplicationExt
{
public static Task<bool> IdleYield()
{
var idleTcs = new TaskCompletionSource<bool>();
if (IsMessagePending())
{
// register for Application.Idle
EventHandler handler = null;
handler = (s, e) =>
{
Application.Idle -= handler;
idleTcs.SetResult(true);
};
Application.Idle += handler;
}
else
idleTcs.SetResult(false);
return idleTcs.Task;
}
public static bool IsMessagePending()
{
// The high-order word of the return value indicates the types of messages currently in the queue.
return 0 != (GetQueueStatus(QS_MASK) >> 16 & QS_MASK);
}
const uint QS_MASK = 0x1FF;
[System.Runtime.InteropServices.DllImport("user32.dll")]
static extern uint GetQueueStatus(uint flags);
}
}
This code is specific to WinForms. Here is a similar approach for WPF.

How to chain tasks within each thread

I have a requirement for copying several hundred tables using bulkcopy.
So far I got the following code.
// from UI main thread
private void test1()
{
LimitedTaskScheduler scheduler = new LimitedTaskScheduler(4);
TaskFactory factory = new TaskFactory(scheduler);
foreach (DataRow row in tabList.Rows) // Loop over the rows.
{
string tabName = row[tabList.Columns["TableName"]].ToString();
factory.StartNew<string>( () =>
{
Console.WriteLine("{0} on thread {1}", tabName, Thread.CurrentThread.ManagedThreadId);
TableCopyer tc1 = new TableCopyer();
// pass progressbar and label into tc1 instance to update the progressbar and label
tc1.Bar1 = bar1;
tc1.L1 = l1;
tc1.EntityCopy(AppInfo.SrcDBMSPath, AppInfo.DestDBMSPath, tabName, "");
return tabName;
});
}
}
// inside TableCopier class
private void OnSqlRowsCopied(object sender, SqlRowsCopiedEventArgs e)
{
try
{
int rowCopied = int.Parse(e.RowsCopied.ToString());
double result = (double.Parse(rowCopied.ToString()) / double.Parse(TotalRows.ToString()));
int prsent = int.Parse(Math.Round((result * 100), 0).ToString());
SetProgressbar(prsent);
}
catch { throw; }
}
LimitedTaskScheduler is coming from
http://msdn.microsoft.com/en-us/library/ee789351.aspx
My questions are
How do I chain the tasks so that each will start only upon the finish of the prior task within each thread? I limited the max parallel thread to 4. I want to chain the tasks within the threads because the tasks being performed within a thread will reuse the same progressbar in a way one finishes and another starts.
I have 4 progress bar painted on UI, bar1, bar2, bar3, bar4. How do I associate each progressbar with a particular thread so that the user can see 4 running progress at the same time?
I think it doesn't make much sense to tie yourself into thinking about specific threads. Instead, what you want is that your tasks run concurrently at most 4 at a time and you want to use your 4 progress bars from those tasks.
What you could do is to put those progress bars into a collection, and then take a progress bar from there at the start of each task and return it back there at the end of the task.
You have to be careful about thread-safety here, so you should either use locks when working with the collection of progress bars, or use a thread-safe collection like ConcurrentQueue. Also, updating the progress bar usually has to be done from the UI thread, so you will have to take care of that too.
How do I chain the tasks so that each will start only upon the finish
of the prior task within each thread? I limited the max parallel
thread to 4. I want to chain the tasks within the threads because the
tasks being performed within a thread will reuse the same progressbar
in a way one finishes and another starts.
You want to chain your tasks together. So essentially there is no parallel processing occurring, except that you wish to process this on a thread separate to the UI. So, why then do you want to spawn a new task for each iteration of your loop if you wan't your loop processing to run sequentially? You could just wrap a normal for loop in a task:
private void test1()
{
Task.Factory.StartNew(() =>
{
foreach (DataRow row in tabList.Rows) // Loop over the rows.
{
string tabName = row[tabList.Columns["TableName"]].ToString();
Console.WriteLine("{0} on thread {1}", tabName, Thread.CurrentThread.ManagedThreadId);
TableCopyer tc1 = new TableCopyer();
// pass progressbar and label into tc1 instance to update the progressbar and label
tc1.Bar1 = bar1;
tc1.L1 = l1;
tc1.EntityCopy(AppInfo.SrcDBMSPath, AppInfo.DestDBMSPath, tabName, "");
}
});
}
I have 4 progress bar painted on UI, bar1, bar2, bar3, bar4. How do I
associate each progressbar with a particular thread so that the user
can see 4 running progress at the same time?
There are many ways to do this and it really depends on your requirements as to which is the best. You could reference the progress bars in a Queue<T>, but only if you are convinced that you will have as many progress bars as you have table rows.
private void test1()
{
Task.Factory.StartNew(() =>
{
var progressBars = new Queue<ProgressBar>(new List<ProgressBar> { bar1, bar2, bar3, bar4 });
foreach (DataRow row in tabList.Rows) // Loop over the rows.
{
// ...
if(progressBars.Count() == 0}
throw new Exception("Not enough progress bars setup!");
tc1.Bar1 = progressBars.Dequeue();
// ...
}
});
}
If tabList.Rows is an IEnumerable, then you can ensure that the queue won't break by using Take():
foreach (DataRow row in tabList.Rows.Take(progressBars.Count()))
{ ... }

Error Cross-thread operation not valid: Control 'CameraViewVS' accessed from a thread other than the thread it was created on. parallel.for

I have a timer to verify one condition every time and show pop up form only once if the condition is verified. I want to verify in parallel all instances, so i used parallel.for, but i have this error "Cross-thread operation not valid: Control 'CameraViewVS' accessed from a thread other than the thread it was created on." in line " frm.WindowState = FormWindowState.Normal;"
this is my code:
public void timer1_Tick(object source, EventArgs e)
{
Parallel.For(0, nbre, l =>
{
cameraInstanceList[l].Start();
if (cameraInstanceList[l].MoveDetection == true)
{
//show the the form S once
foreach (Form S in Application.OpenForms)
{
var frm = S as Formes.CameraViewVS;
if (frm != null && frm.IP == cameraInstanceList[l].adresse)
{
cameraInstanceList[l].MoveDetection = false;
frm.WindowState = FormWindowState.Normal;
frm.Activate();
return;
}
}
f1 = new Formes.CameraViewVS(cameraInstanceList[l],
adresseIPArray[l]);
f1.Show(this);
}
}
);
Most properties on WinForm object instances need to be accessed from the thread that they were created on. You can use the Control.InvokeRequired property to determine if you need to use the control (or form) Invoke method to execute the code on the UI thread.
It is also a good practise to create most WinForm controls on the main UI thread, and not on any thread pool threads. In WinForms applications, you can use the SynchronizationContext to ensure some code, such as creating a form, is called on the UI thread.
EDIT: changed so that the method doesn't return after movement detected.
public void timer1_Tick(object source, EventArgs e)
{
// assume this is being called on the UI thread, and save the thread synchronization context
var uiContext = SynchronizationContext.Current;
Parallel.For(0, nbre, l =>
{
while (true)
{
Thread.Sleep(250); // <--- sleep for 250 ms to avoid "busy" wait
cameraInstanceList[l].Start();
if (cameraInstanceList[l].MoveDetection == true)
{
// capture instances used in closures below
var cameraInstance = cameraInstanceList[l];
var ipAdresse = adresseIPArray[l];
//show the the form S once
foreach (Form S in Application.OpenForms)
{
var frm = S as Formes.CameraViewVS;
if (frm != null)
{
// create delegate to be invoked on form's UI thread.
var action = new Action(() =>
{
if (frm.IP == cameraInstance.adresse)
{
cameraInstance.MoveDetection = false;
frm.WindowState = FormWindowState.Normal;
frm.Activate();
}
};
if (frm.InvokeRequired)
frm.Invoke(action);
else
action();
continue; // <--- go back to the top of the while loop
// and wait for next detection
}
}
// create delegate to create new form on UI thread.
var createNewFormCallback = new SendOrPostCallback((o) =>
{
f1 = new Formes.CameraViewVS(cameraInstance, ipAdresse);
f1.Show(this);
};
// and invoke the delegate on the ui thread
uiContext.Send(createNewFormCallback, null);
}
}
}
);
}
Thomas is very close to right answer ,Because Every Control runs in a different thread .You should just write a code for context-switching of resources which is being used by Controls
Thread ..Don't worry you have a lot of facility for this in c sharp.Just use BeginInvoke and Invoke and i hope you would be able to resolve your problem.Write this in place of your old code block ..
var action = new Action(() =>
{
if (frm.IP == cameraInstance.adresse)
{
cameraInstance.MoveDetection = false;
frm.WindowState = FormWindowState.Normal;
frm.Activate();
}
};
if (frm.InvokeRequired)
frm.BeginInvoke(action);
else
frm.Invoke(action);

Reading output from console application and WPF plotting async

I have a console application which outputs about 160 lines of info every 1 second.
The data output is points that can be used to plot on a graph.
In my WPF application, I've successfully have this hooked up and the data output by the console application is being plotted, however, after about 500 or so data points, I see significant slow down in the application and UI thread lockups.
I assume this is due to the async operations I'm using:
BackgroundWorker worker = new BackgroundWorker();
worker.DoWork += delegate(object s, DoWorkEventArgs args)
{
_process = new Process();
_process.StartInfo.FileName = "consoleApp.exe";
_process.StartInfo.UseShellExecute = false;
_process.StartInfo.RedirectStandardOutput = true;
_process.StartInfo.CreateNoWindow = true;
_process.EnableRaisingEvents = true;
_process.OutputDataReceived += new DataReceivedEventHandler(SortOutputHandler);
_process.Start();
_process.BeginOutputReadLine();
_watch.Start();
};
worker.RunWorkerAsync();
And the handler that is taking care of parsing and plotting the data:
private void SortOutputHandler(object sendingProcess, DataReceivedEventArgs outLine)
{
if (!String.IsNullOrEmpty(outLine.Data))
{
var xGroup = Regex.Match(outLine.Data, "x: ?([-0-9]*)").Groups[1];
int x = int.Parse(xGroup.Value);
var yGroup = Regex.Match(outLine.Data, "y: ?([-0-9]*)").Groups[1];
int y = int.Parse(yGroup.Value);
var zGroup = Regex.Match(outLine.Data, "z: ?([-0-9]*)").Groups[1];
int z = int.Parse(zGroup.Value);
Reading reading = new Reading()
{
Time = _watch.Elapsed.TotalMilliseconds,
X = x,
Y = y,
Z = z
};
Dispatcher.Invoke(new Action(() =>
{
_readings.Enqueue(reading);
_dataPointsCount++;
}), System.Windows.Threading.DispatcherPriority.Normal);
}
}
_readings is a custom ObservableQueue<Queue> as defined in this answer. I've modified it so that only 50 items can be in the queue at a time. So if a new item is being added and the queue count >= 50, a Dequeue() is called before an Enqueue().
Is there any way I can improve the performance or am I doomed because of how much the console app outputs?
From what I can tell here is what it looks like is going on:
IU thread spins up a background worker to launch the console App.
It redirects the output of the Console and handles it with a handler on the UI thread
The handler on the UI thread then calls Dispatcher.Invoke 160 times a second to update a queue object on the same thread.
After 50 calls the queue starts blocking while items are dequeued by the UI
The trouble would seem to be:
Having the UI thread handle the raw output from the console and the queue and the update to the Graph.
There is also a potential problem with blocking between enqueue and dequeue once the UI is over 50 data items behind that might be leading to a cascading failure. (I can't see enough of the code to be sure of that)
Resolution:
Start another background thread to manage the data from the console app
The new thread should: Create the Queue; handle the OutputDataReceived event; and launch the console app process.
The Event Handler should not use Dispatcher.Invoke to update the Queue. A direct threadsafe call should be used.
The Queue really needs to be non blocking when updating the UI, but I don't really have enough information about how that's being implemented to comment.
Hope this helps
-Chris
I suspect that there's a thread starvation issue happening on the UI thread as your background thread is marshaling calls to an observable collection that is possibly forcing the underlying CollectionView to be recreated each time. This can be a pretty expensive operation.
Depending how you've got your XAML configured is also a concern. The measure / layout changes alone could be killing you. I would imagine that at the rate the data is coming in, the UI hasn't got a chance to properly evaluate what's happening to the underlying data.
I would suggest not binding the View to the Queue directly. Instead of using an Observable Queue as you've suggested, consider:
Use a regular queue that caps content at 50 items. Don't worry about the NotifyCollectionChanged event happening on the UI thread. You also won't have to marshal each item to the UI thread either.
Expose a CollectionViewSource object in your ViewModel that takes the Queue as its collection.
Use a timer thread on the UI to manually force a refresh of the CollectionViewSource. Start with once a second and decrease the interval to see what your XAML and machine can handle. In this fashion, you control when the CollectionView is created and destroyed.
You could try passing the processed data onto the UI Thread from the BackgroundWorker ProgressChanged event.
Something like....
// Standard warnings apply: not tested, no exception handling, etc.
var locker = new object();
var que = new ConcurrentQueue<string>();
var worker = new BackgroundWorker();
var proc = new Process();
proc.StartInfo.FileName = "consoleApp.exe";
proc.StartInfo.UseShellExecute = false;
proc.StartInfo.RedirectStandardOutput = true;
proc.StartInfo.CreateNoWindow = true;
proc.EnableRaisingEvents = true;
proc.OutputDataReceived +=
(p, a) =>
{
que.Enqueue(a.Data);
Monitor.Pulse(locker);
};
worker.DoWork +=
(s, e) =>
{
var watch = Stopwatch.StartNew();
while (!e.Cancel)
{
while (que.Count > 0)
{
string data;
if (que.TryDequeue(out data))
{
if (!String.IsNullOrEmpty(data))
{
var xGroup = Regex.Match(data, "x: ?([-0-9]*)").Groups[1];
int x = int.Parse(xGroup.Value);
var yGroup = Regex.Match(data, "y: ?([-0-9]*)").Groups[1];
int y = int.Parse(yGroup.Value);
var zGroup = Regex.Match(data, "z: ?([-0-9]*)").Groups[1];
int z = int.Parse(zGroup.Value);
var reading = new Reading()
{
Time = watch.Elapsed.TotalMilliseconds,
X = x,
Y = y,
Z = z
};
worker.ReportProgress(0, reading);
}
}
else break;
}
// wait for data or timeout and check if the worker is cancelled.
Monitor.Wait(locker, 50);
}
};
worker.ProgressChanged +=
(s, e) =>
{
var reading = (Reading)e.UserState;
// We are on the UI Thread....do something with the new reading...
};
// start everybody.....
worker.RunWorkerAsync();
proc.Start();
proc.BeginOutputReadLine();
You can simply store the points in a list and call the dispatcher only when you have e.g. reached 160 points so you do not create to many update messages. Currently you are causing a window message every 6ms which is way too much. When you update the UI e.g. every second or every 160 points things will be much smoother. If the notifications are still too much you need to have a look how you can suspend redrawing your control while you update the UI with 160 data points and resume drawing afterwards so you do not get heavy flickering.
List<Reading> _Readings = new List<Reading>();
DateTime _LastUpdateTime = DateTime.Now;
TimeSpan _UpdateInterval = new TimeSpan(0,0,0,0,1*1000); // Update every 1 second
private void SortOutputHandler(object sendingProcess, DataReceivedEventArgs outLine)
{
if (!String.IsNullOrEmpty(outLine.Data))
{
var xGroup = Regex.Match(outLine.Data, "x: ?([-0-9]*)").Groups[1];
int x = int.Parse(xGroup.Value);
var yGroup = Regex.Match(outLine.Data, "y: ?([-0-9]*)").Groups[1];
int y = int.Parse(yGroup.Value);
var zGroup = Regex.Match(outLine.Data, "z: ?([-0-9]*)").Groups[1];
int z = int.Parse(zGroup.Value);
Reading reading = new Reading()
{
Time = _watch.Elapsed.TotalMilliseconds,
X = x,
Y = y,
Z = z
};
// create a batch of readings until it is time to send it to the UI
// via ONE window message and not hundreds per second.
_Readings.Add(reading);
DateTime current = DateTime.Now;
if( current -_LastUpdateTime > _UpdateInterval ) // update ui every second
{
_LastUpdateTime = current;
List<Reading> copy = _Readings; // Get current buffer and make it invisible to other threads by creating a new list.
// Since this is the only thread that does write to it this is a safe operation.
_Readings = new List<Reading>(); // publish a new empty list
Dispatcher.Invoke(new Action(() =>
{
// This is called as part of a Window message in the main UI thread
// once per second now and not every 6 ms. Now we can upate the ui
// with a batch of 160 points at once.
// A further optimization would be to disable drawing events
// while we add the points to the control and enable it after
// the loop
foreach(Reading reading in copy)
{
_readings.Enqueue(reading);
_dataPointsCount++;
}
}),
System.Windows.Threading.DispatcherPriority.Normal);
}
}
}

Resources