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);
}
}
}
Related
I’ve run into a performance obstacle and I’m uncertain of the cause, all of this is running under VS2022 & Net6.0. As this is my 1st time using this combination of a modal windows form, and progress bar, with the work running on a background thread and two Progress objects updating the UI, the progress bar, and a text label, I don’t know where to attack the problem. Prior to placing the workload on a background thread, everything was snappy, searching a thousand files with about 600 lines of text in each, in about a minute. Naturally, the windows form was frozen during this, which is why the workload was placed on a background thread.
After doing so, the workload will be 25-50% complete before the UI starts displaying the values from the Progress objects, and overall, the entire process now takes 10x as long to complete. Progress objects aren’t skipping over any values sent to them, the UI thread just seems slow in getting the information. Likewise, if I try to drag the modal form to a new spot on the desktop it’s unresponsive for 20—30 seconds before it finally moves. One more thing, I can step through the code on the background thread and see it calling the Progress updaters, but the UI thread is just very slow in responding to them.
I could use some suggestions on how to uncover the problem or if clearly evident, point out where the likely problem could be. Here are the essential controls and methods used.
public class SearchProgressForm : Form
{
private System.Windows.Forms.Button btnSearch = new Button();
private System.Windows.Forms.TextBox txtTextSearch = new TextBox();
private System.Windows.Forms.Label lblSearchFile = new Label();
private System.Windows.Forms.ProgressBar SearchProgressBar = new ProgressBar();
public event LogSearchEventHandler SearchSucceededEvent;
protected void OnSearchSucceeded(LogSearchEventArguments p_eventArguments)
{
LogSearchEventHandler handler = SearchSucceededEvent;
if (handler != null)
{
handler(this, p_eventArguments);
}
}
private void InitializeComponent()
{
this.btnSearch.Name = "btnSearch";
this.btnSearch.Text = "Search";
this.btnSearch.Click += new System.EventHandler(this.btnSearch_Click);
this.lblSearchFile.Text = "Searching File: ";
this.txtTextSearch.Text = "search string";
}
public SearchProgressForm() { }
private void btnSearch_Click(object sender, EventArgs e)
{
this.SearchByText(this.txtTextSearch.Text);
}
private void SearchByText(string p_searchParameter)
{
// Setup a progress report for thr ProgressBar
var _progressBarUpdate = new Progress<int>(value =>
{
this.SearchProgressBar.Value = value;
this.SearchProgressBar.Refresh();
});
var _progressFileNameUpdate = new Progress<string>(value =>
{
this.lblSearchFile.Text = "Searching File For : " + value;
this.lblSearchFile.Refresh();
});
// Start search on a backgroud thread and report progress as it occurs
Task.Run(async () => await this.SearchByStringAsync(p_searchParameter, _progressBarUpdate, _progressFileNameUpdate));
}
private async Task SearchByStringAsync(string p_searchParameter, IProgress<int> p_progressBar, IProgress<string> p_progressFileName)
{
await Task.Delay(1);
TextFileReader textFileReader = new TextFileReader();
LogSearchEventArguments logSearchEventArguments = null;
long _sessionloopCount = 0;
long _totalTextLinesCount = this.GetTotalSearchCount(p_searchParameter, SearchType.TextString);
// Get file names from SQL table
var _logFiles = DataOperations.LogFileSortableList(null);
foreach (var log in _logFiles)
{
// Format a file name to be read from the file system
string _fileName = log.Directory + "\\" + log.FileName;
p_progressFileName.Report(log.FileName);
// If we've raised an event for this file, then stop iterating over remaning text
if (logSearchEventArguments != null)
{
logSearchEventArguments = null;
break;
}
// Read in file contents from file system
List<string> _fileContents = textFileReader.ReadAndReturnStringList(_fileName);
long _fileTotalRecordCount = _fileContents.Count;
long _fileRecordCount = 0;
foreach (var _line in _fileContents)
{
if (_line.ToUpper().Contains(p_searchParameter.ToUpper()))
{
// Raise an event so search parameter and file name can be captured in another form
logSearchEventArguments =
new LogSearchEventArguments
(
"TextSearch", p_searchParameter, SearchType.TextString, true, log,
new DateTime(
Convert.ToInt32("20" + log.FileName.Substring(14, 2)),
Convert.ToInt32(log.FileName.Substring(16, 2)),
Convert.ToInt32(log.FileName.Substring(18, 2)))
);
// We found a match, so no further searching is needed in this log file,
// and it's been flagged in the DB, so raise the event to save search parameter and file name
// then break out of this loop to get the next file to search in.
this.OnSearchSucceeded(logSearchEventArguments);
break;
}
// These calcs are based on actual searches performed
_fileRecordCount++;
_sessionloopCount++;
p_progressBar.Report(Convert.ToInt32((_sessionloopCount * 100) / _totalTextLinesCount));
}
// Because we exit a search as soon as the 1st match is made, need to resynch all counts
// and update the progress bar accordingly
if (_fileRecordCount < _fileTotalRecordCount)
{
long _countDifference = _fileTotalRecordCount - _fileRecordCount;
// Add count difference to sessionLoopCount and update progress bar
_sessionloopCount += _countDifference;
p_progressBar.Report(Convert.ToInt32((_sessionloopCount * 100) / _totalTextLinesCount));
}
}
//Search is complete set Progress to 100% and report before exiting
p_progressBar.Report(100);
// Close the modal SearchForm and exit
this.Close();
}
}
I solved this problem but I'm still not certain of what caused it. I eliminated the method "private void SearchByText(string p_searchParameter)" and moved the code there into the btnSearch_Click event handler so I could call my background worker "SearchByStringAsync" directly from the button click event handler.
I also updated the EFCore NuGet Packages, which were version Net6.0 to version 6.0.4, because of single line of code in my Async background method, "var _logFiles = DataOperations.LogFileSortableList(null)".
That call returned a Sortable BindingList, using BindingList <T>. Between the NuGet updates and a minor change on a custom comparer method in my BindingList <T> class, the windows modal form now updates the ProgressBar and Label text as expected, and the form now responds immediately to user interaction.
I am trying to implement a thread-safe solution to keep a count of successful tasks that have been completed, which will ultimately get bound to label displayed on the UI. However, when I use the AtomicInteger below it locks up my UI when the tasks start running, however, if I remove all AtomicInteger refs everything works fine. Is there a non-blocking, thread-safe way which this can be accomplished?
public void handleSomeButtonClick(){
if(!dataModel.getSomeList().isEmpty()) {
boolean unlimited = false;
int count = 0;
AtomicInteger successCount = new AtomicInteger(0);
if(countSelector.getValue().equalsIgnoreCase("Unlimited"))
unlimited = true;
else
count = Integer.parseInt(countSelector.getValue());
while(unlimited || successCount.get() < count) {
Task task = getSomeTask();
taskExecutor.submit(task);
task.setOnSucceeded(event -> {
if (task.getValue())
log.info("Successfully Completed Task | Total Count: " + successCount.incrementAndGet());
else
log.error("Failed task");
});
}
}
}
Your loop waits for a certain number of tasks to be completed. It may even be an infinite loop.
This is not a good idea:
You block the calling thread which seems to be the JavaFX application thread.
You don't have any control of how many tasks are submitted. count could be 3, but since you only schedule the tasks in the loop, 1000 or more tasks could be created&scheduled before the first one completes.
Furthermore if you use onSucceeded/onFailed, you don't need to use AtomicInteger or any similar kind of synchronisation, since those handlers all run on the JavaFX application thread.
Your code could be rewritten like this:
private int successCount;
private void scheduleTask(final boolean unlimited) {
Task task = getSomeTask();
task.setOnSucceeded(event -> {
// cannot get a Boolean from a raw task, so I assume the task is successfull iff no exception happens
successCount++;
log.info("Successfully Completed Task | Total Count: " + successCount);
if (unlimited) {
// submit new task, if the number of tasks is unlimited
scheduleTask(true);
}
});
// submit new task on failure
task.setOnFailed(evt -> scheduleTask(unlimited));
taskExecutor.submit(task);
}
public void handleSomeButtonClick() {
if(!dataModel.getSomeList().isEmpty()) {
successCount = 0;
final boolean unlimited;
final int count;
if(countSelector.getValue().equalsIgnoreCase("Unlimited")) {
unlimited = true;
count = 4; // set limit of number of tasks submitted to the executor at the same time
} else {
count = Integer.parseInt(countSelector.getValue());
unlimited = false;
}
for (int i = 0; i < count; i++) {
scheduleTask(unlimited);
}
}
}
Note: This code runs the risk of handleButtonClick being clicked multiple times before the previous tasks have been completed. You should either prevent scheduling new tasks before the old ones are completed or use some reference type containing an int instead for the count, create this object in handleSomeButtonClick and pass this object to scheduleTask.
Your UI lock up means you do the counting(successCount.get() < count) in your FX application thread. I cannot understand why you keep submit the task in the while loop,
which one do you want to do? (1) start X(e.g. 10) task and count how many task is success. or (2) just keep starting new task and see the count go up.
if(2) then run the whole while loop in a background thread, update the UI in a Platform->runlater().
if(1) use the Future / CompletableFuture, or more powerful version Future in 3rd party package like vavr.
Your problem is future.get() block and wait for result.
This will be simple if you use Vavr library.
Because it can attach a code to its future which run automatically when success or fail.
So you don't have to wait.
Here is a example which using Vavr's future.
CheckedFunction0<String> thisIsATask = () -> {
if ( /*do something*/ ){
throw new Exception("Hey");
}
return "ABC";
};
List<Future<String>> futureList = new ArrayList<>();
for (int x = 0; x < 10; x++) {
futureList.add(Future.of(getExecutorService(), thisIsATask));
}
futureList.forEach((task) -> {
// This will run if success
task.onSuccess(s -> s.equals("ABC") ? Platform.runLater(()->UpdateCounter()) : wtf());
// Your get the exception if it is fail;
task.onFailure(e -> e.printStackTrace());
// task.onComplete() will run on any case when complete
});
This is not blocking, the code at onSucess onFailure or onComplete will run when the task is finish or an exception is catch.
Note: Future.of will use the executorService you pass in to run each task at new thread, the code you provide at onSuccess will continue to run at that thread once the task is done so if you calling javafx remember the Platform.runLater()
Also if you want to run something when all task is finish, then
// the code at onComplete will run when tasks all done
Future<Seq<String>> all = Future.sequence(futureList);
all.onComplete((i) -> this.btnXYZ.setDisable(false));
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.
I am running into some strange behavior in the backgroundworker class that leads me to believe that I don't fully understand how it works. I assumed that the following code sections were more or less equal except for some extra features that the BackgroundWorker implements (like progress reporting, etc.):
section 1:
void StartSeparateThread(){
BackgroundWorker bw = new BackgroundWorker();
bw.DoWork += new DoWorkEventHandler(bw_DoWork);
bw.RunWorkerAsync();
}
void bw_DoWork(object sender, DoWorkEventArgs e)
{
//Execute some code asynchronous to the thread that owns the function
//StartSeparateThread() but synchronous to itself.
var SendCommand = "SomeCommandToSend";
var toWaitFor = new List<string>(){"Various","Possible","Outputs to wait for"};
var SecondsToWait = 30;
//this calls a function that sends the command over the NetworkStream and waits
//for various responses.
var Result=SendAndWaitFor(SendCommand,toWaitFor,SecondsToWait);
}
Section 2:
void StartSeparateThread(){
Thread pollThread = new Thread(new ThreadStart(DoStuff));
pollThread.Start();
}
void DoStuff(object sender, DoWorkEventArgs e)
{
//Execute some code asynchronous to the thread that owns the function
//StartSeparateThread() but synchronous to itself.
var SendCommand = "SomeCommandToSend";
var toWaitFor = new List<string>(){"Various","Possible","Outputs to wait for"};
var SecondsToWait = 30;
//this calls a function that sends the command over the NetworkStream and waits
//for various responses.
var Result=SendAndWaitFor(SendCommand,toWaitFor,SecondsToWait);
}
I was using Section 1 to run some code that sent a string over a networkstream and waited for a desired response string, capturing all output during that time. I wrote a function to do this that would return the networkstream output, the index of the the sent string, as well as the index of the desired response string. I was seeing some strange behavior with this so I changed the function to only return when both the send string and the output string were found, and that the index of the found string was greater than the index of the sent string. It would otherwise loop forever (just for testing). I would find that the function would indeed return but that the index of both strings were -1 and the output string was null or sometimes filled with the expected output of the previous call. If I were to make a guess about what was happening, it would be that external functions called from within the bw_DoWork() function are run asynchronously to the thread that owns the bw_DoWork() function. As a result, since my SendAndWaitFor() function was called multiple times in succession. the second call would be run before the first call finished, overwriting the results of the first call after they were returned but before they could be evaluated. This seems to make sense because the first call would always run correctly and successive calls would show the strange behavior described above but it seems counter intuitive to how the BackgroundWorker class should behave. Also If I were to break within the SendAndWaitFor function, things would behave properly. This again leads me to believe there is some multi-threading going on within the bwDoWork function itself.
When I change the code in the first section above to the code of the second section, things work entirely as expected. So, can anyone who understands the BackgroundWorker class explain what could be going on? Below are some related functions that may be relevant.
Thanks!
public Dictionary<string, string> SendAndWaitFor(string sendString, List<string> toWaitFor, int seconds)
{
var toReturn = new Dictionary<string, string>();
var data = new List<byte>();
var enc = new ASCIIEncoding();
var output = "";
var FoundString = "";
//wait for current buffer to clear
output = this.SynchronousRead();
while(!string.IsNullOrEmpty(output)){
output = SynchronousRead();
}
//output should be null at this point and the buffer should be clear.
//send the desired data
this.write(enc.GetBytes(sendString));
//look for all desired strings until timeout is reached
int sendIndex=-1;
int foundIndex = -1;
output += SynchronousRead();
for (DateTime start = DateTime.Now; DateTime.Now - start < new TimeSpan(0, 0, seconds); )
{
//wait for a short period to allow the buffer to fill with new data
Thread.Sleep(300);
//read the buffer and add it to the output
output += SynchronousRead();
foreach (var s in toWaitFor)
{
sendIndex = output.IndexOf(sendString);
foundIndex = output.LastIndexOf(s);
if (foundIndex>sendIndex)
{
toReturn["sendIndex"] = sendIndex.ToString();
toReturn["foundIndex"] = sendIndex.ToString();
toReturn["Output"] = output;
toReturn["FoundString"] = s;
return toReturn;
}
}
}
//Set this to loop infinitely while debuging to make sure the function was only
//returning above
while(true){
}
toReturn["sendIndex"]="";
toReturn["foundIndex"]="";
toReturn["Output"] =output;
toReturn["FoundString"] = "";
return toReturn;
}
public void write(byte[] toWrite)
{
var enc = new ASCIIEncoding();
var writeString = enc.GetString(toWrite);
var ns = connection.GetStream();
ns.Write(toWrite, 0, toWrite.Length);
}
public string SynchronousRead()
{
string toReturn = "";
ASCIIEncoding enc = new ASCIIEncoding();
var ns = connection.GetStream();
var sb = new StringBuilder();
while (ns.DataAvailable)
{
var buffer = new byte[4096];
var numberOfBytesRead = ns.Read(buffer, 0, buffer.Length);
sb.AppendFormat("{0}", Encoding.ASCII.GetString(buffer, 0, numberOfBytesRead));
toReturn += sb.ToString();
}
return toReturn;
}
All data to be used by a background worker should be passed in through the DoWorkEventArgs and nothing should be pulled off of the class (or GUI interface).
In looking at your code I could not identify where the property(?) connnection was being created. My guess is that connection is created on a different thread, or may be pulling read information, maybe from a GUI(?) and either one of those could cause problems.
I suggest that you create the connection instance in the dowork event and not pull an existing one off of a different thread. Also verify that the data connection works with does not access any info off of a GUI, but its info is passed in as its made.
I discuss an issue with the Background worker on my blog C# WPF: Linq Fails in BackgroundWorker DoWork Event which might show you where the issue lies in your code.
I have a Silverlight app. that has a basic animation where a rectangle is animated to a new position. The animation consists of two DoubleAnimation() - one transforms the X, the other transforms the Y. It works OK.
I basically want to block any other calls to this animate method until the first two animations have completed. I see that the DoubleAnimation() class has a Completed event it fires but I haven't been successful in constructing any kind of code that successfully blocks until both have completed.
I attempted to use Monitor.Enter on a private member when entering the method, then releasing the lock from one of the animations Completed event, but my attempts at chaining the two events (so the lock isn't released until both have completed) haven't been successful.
Here's what the animation method looks like:
public void AnimateRectangle(Rectangle rect, double newX, double newY)
{
var xIsComplete = false;
Duration duration = new Duration(new TimeSpan(0, 0, 0, 1, 350));
var easing = new ElasticEase() { EasingMode = EasingMode.EaseOut, Oscillations = 1, Springiness = 4 };
var animateX = new DoubleAnimation();
var animateY = new DoubleAnimation();
animateX.EasingFunction = easing;
animateX.Duration = duration;
animateY.EasingFunction = easing;
animateY.Duration = duration;
var sb = new Storyboard();
sb.Duration = duration;
sb.Children.Add(animateX);
sb.Children.Add(animateY);
Storyboard.SetTarget(animateX, rect);
Storyboard.SetTargetProperty(animateX, new PropertyPath("(Canvas.Left)"));
Storyboard.SetTarget(animateY, rect);
Storyboard.SetTargetProperty(animateY, new PropertyPath("(Canvas.Top)"));
animateX.To = newX;
animateY.To = newY;
sb.Begin();
}
EDIT (added more info)
I ran into this initially because I was calling this method from another method (as it processed items it made a call to the animation). I noticed that the items didn't end up where I expected them to. The new X/Y coordinates I pass in are based on the items current location, so if it was called multiple times before it finished, it ended up in the wrong location. As a test I added a button that only ran the animation once. It worked. However, if I click on the button a bunch of times in a row I see the same behavior as before: items end up in the wrong location.
Yes, it appears Silverlight animations are run on the main UI thread. One of the tests I tried I added two properties that flagged whether both animations had completed yet. In the AnimateRectange() method I checked them inside of a while loop (calling Thread.Sleep). This loop never completed (so it's definitely on the same thread).
So I created a queue to process the animations in order:
private void ProcessAnimationQueue()
{
var items = this.m_animationQueue.GetEnumerator();
while (items.MoveNext())
{
while (this.m_isXanimationInProgress || this.m_isYanimationInProgress)
{
System.Threading.Thread.Sleep(100);
}
var item = items.Current;
Dispatcher.BeginInvoke(() => this.AnimateRectangle(item.Rect.Rect, item.X, item.Y));
}
}
Then I call my initial routine (which queues up the animations) and call this method on a new thread. I see the same results.
As far as I am aware all of the animations in Silverlight are happening on the UI thread anyway. I am guessing that only the UI thread is calling this animation function anyway, so I am not sure that using locking will help. Do you really want to be blocking the entire thread or just preventing another animation from starting?
I would suggest something more like this:
private bool isAnimating = false;
public void AnimateRectangle(Rectangle rect, double newX, double newY)
{
if (isAnimating)
return;
// rest of animation code
sb.Completed += (sender, e) =>
{
isAnimating = false;
};
isAnimating = true;
sb.Begin();
}
Just keep track of whether or not you are currently animating with a flag and return early if you are. If you don't want to lose potential animations your other option is to keep some kind of a queue for animation which you could check/start when each animation has completed.
This question really peaked my interest. In fact I'm going to include it in my next blog post.
Boiling it down, just to be sure we are talking about the same thing, fundementally you don't want to block the call to AnimateRectangle you just want to "queue" the call so that once any outstanding call has completed its animation this "queued" call gets executed. By extension you may need to queue several calls if a previous call hasn't even started yet.
So we need two things:-
A means to treat what are essentially asynchronous operations (sb.Begin to Completed event) as a sequential operation, one operation only starting when the previous has completed.
A means to queue additional operations when one or more operations are yet to complete.
AsyncOperationService
Item 1 comes up in a zillion different ways in Silverlight due to the asynchronous nature of so many things. I solve this issue with a simple asynchronous operation runner blogged here. Add the AsyncOperationService code to your project.
AsyncOperationQueue
Its item 2 that really took my interest. The variation here is that whilst an existing set of operations are in progress there is demand to add another. For a general case solution we'd need a thread-safe means of including another operation.
Here is the bare-bones of a AsyncOperationQueue:-
public class AsyncOperationQueue
{
readonly Queue<AsyncOperation> myQueue = new Queue<AsyncOperation>();
AsyncOperation myCurrentOp = null;
public void Enqueue(AsyncOperation op)
{
bool start = false;
lock (myQueue)
{
if (myCurrentOp != null)
{
myQueue.Enqueue(op);
}
else
{
myCurrentOp = op;
start = true;
}
}
if (start)
DequeueOps().Run(delegate { });
}
private AsyncOperation GetNextOperation()
{
lock (myQueue)
{
myCurrentOp = (myQueue.Count > 0) ? myQueue.Dequeue() : null;
return myCurrentOp;
}
}
private IEnumerable<AsyncOperation> DequeueOps()
{
AsyncOperation nextOp = myCurrentOp;
while (nextOp != null)
{
yield return nextOp;
nextOp = GetNextOperation();
}
}
}
Putting it to use
First thing to do is convert your existing AnimateRectangle method into a GetAnimateRectangleOp that returns a AsyncOperation. Like this:-
public AsyncOperation GetAnimateRectangleOp(Rectangle rect, double newX, double newY)
{
return (completed) =>
{
// Code identical to the body of your original AnimateRectangle method.
sb.Begin();
sb.Completed += (s, args) => completed(null);
};
}
We need to hold an instance of the AsyncOperationQueue:-
private AsyncOperationQueue myAnimationQueue = new AsyncOperationQueue();
Finally we need to re-create AnimateRectangle that enqueues the operation to the queue:-
public void AnimateRectangle(Rectangle rect, double newX, double newY)
{
myAnimationQueue.Enqueue(GetAnimateRectangleOp(rect, newX, newY)
}