Unexpected behavior in async method - c#

I am currently experiencing some unexpected/unwanted behavior with an aync method I am trying to use. The async method is RecognizeAsync. I am unabled to await this method since it returns void. What is happening, is that ProcessAudio method will be called first and will seemingly run to completion however the webpage never returns my "Contact" view as it should or errors out. After the method runs to completion, the breakpoints in my handlers start being hit. If I let it play through to completion, no redirect will ever happen- in the network tab in chrome debugger, the "status" will stay marked as pending and just hang there. I believe my issue is being caused by issues with asynchronousity but have been unable to found out what exactly it is.
All help is appreciated.
[HttpPost]
public async Task<ActionResult> ProcessAudio()
{
SpeechRecognitionEngine speechEngine = new SpeechRecognitionEngine();
speechEngine.SetInputToWaveFile(Server.MapPath("~/Content/AudioAssets/speechSample.wav"));
var grammar = new DictationGrammar();
speechEngine.LoadGrammar(grammar);
speechEngine.SpeechRecognized += new EventHandler<SpeechRecognizedEventArgs>(SpeechRecognizedHandler);
speechEngine.SpeechHypothesized += new EventHandler<SpeechHypothesizedEventArgs>(SpeechHypothesizedHandler);
speechEngine.RecognizeAsync(RecognizeMode.Multiple);
return View("Contact", vm); //first breakpoint hit occurs on this line
//but it doesnt seem to be executed?
}
private void SpeechRecognizedHandler(object sender, EventArgs e)
{
//do some work
//3rd breakpoint is hit here
}
private void SpeechHypothesizedHandler(object sender, EventArgs e)
{
//do some different work
//2nd breakpoint is hit here
}
UPDATE: based on suggestions, I have changed my code to (in ProcessAudio):
using (speechEngine)
{
speechEngine.SetInputToWaveFile(Server.MapPath("~/Content/AudioAssets/speechSample.wav"));
var grammar = new DictationGrammar();
speechEngine.LoadGrammar(grammar);
speechEngine.SpeechRecognized += new EventHandler<SpeechRecognizedEventArgs>(SpeechRecognizedHandler);
speechEngine.SpeechHypothesized += new EventHandler<SpeechHypothesizedEventArgs>(SpeechHypothesizedHandler);
var tcsRecognized = new TaskCompletionSource<EventArgs>();
speechEngine.RecognizeCompleted += (sender, eventArgs) => tcsRecognized.SetResult(eventArgs);
speechEngine.RecognizeAsync(RecognizeMode.Multiple);
try
{
var eventArgsRecognized = await tcsRecognized.Task;
}
catch(Exception e)
{
throw (e);
}
}
and this is resulting in some wrong behavior:
The return View("Contact",vm) breakpoint will now be hit AFTER the handlers are finished firing however there is still no redirect that ever happens. I am never directed to my Contact page. I just si ton my original page indefinitely just like before.

You're going too early. The speech engine probably hasn't even started by the time you hit the return View line.
You need to wait until the final event is fired from the speech engine. The best approach would be to convert from the event based asynchrony to TAP-based asynchrony.
This can be achieved by using TaskCompletionSource<T>
Let's deal with (what I believe) should be the last event to fire after speechEngine.RecognizeAsync is called, i.e. SpeechRecognized. I'm assuming that this is the event that fires when the final result has been calculated by the speech engine.
So, first:
var tcs = new TaskCompletionSource<EventArgs>();
now lets hook it up to complete when SpeechRecognized is fired, using inline lambda-style method declaration:
speechEngine.SpeechRecognized += (sender, eventArgs) => tcs.SetResult(eventArgs);
(...wait... what happens if no speech was recognized? We'll also need to hook up the SpeechRecognitionRejected event and define a custom Exception subclass for this type of event... here I'll just call it RecognitionFailedException. Now we're trapping all possible outcomes of the recognition process, so we would hope that the TaskCompletionSource would complete in all outcomes.)
speechEngine.SpeechRecognitionRejected += (sender, eventArgs) =>
tcs.SetException(new RecognitionFailedException());
then
speechEngine.RecognizeAsync(RecognizeMode.Multiple);
now, we can await the Task property of our TaskCompletionSource:
try
{
var eventArgs = await tcs.Task;
}
catch(RecognitionFailedException ex)
{
//this would signal that nothing was recognized
}
do some processing on the EventArgs that is the Task's result, and return a viable result back to the client.
In the process of doing this, you are creating IDisposable instances that will need to be properly disposed.
So:
using(SpeechRecognitionEngine speechEngine = new SpeechRecognitionEngine())
{
//use the speechEngine with TaskCompletionSource
//wait until it's finished
try
{
var eventArgs = await tcs.Task;
}
catch(RecognitionFailedException ex)
{
//this would signal that nothing was recognized
}
} //dispose

if anyone is curious- i solved my issue by doing the following:
I changed to using Recognize() instead of RecognizeAsync(..) which lead to InvalidOperationException due to async events trying to be executed at an "invalid time in the pages lifecycle". To overcome this, I wrapped my operations in a thread and joined the thread back to the main thread directly after running it. Code below:
using (speechEngine)
{
var t = new Thread(() =>
{
speechEngine.SetInputToWaveFile(#"C:\AudioAssets\speechSample.wav");
speechEngine.LoadGrammar(dictationGrammar);
speechEngine.SpeechRecognized += new EventHandler<SpeechRecognizedEventArgs>(SpeechRecognizedHandler);
speechEngine.SpeechHypothesized += new EventHandler<SpeechHypothesizedEventArgs>(SpeechHypothesizedHandler);
speechEngine.Recognize();
});
t.Start();
t.Join();
}
}

Related

Joining a worker thread in a windows forms app

My program works like this:
I press a radio button which opens the port.
Next i press a button "Read" which starts a thread that reads data continously from the Serial Port using port.ReadLine() and prints it in a textbox;
I have another radio which should first join the thread and after that close the port;the problem is the printing goes well until i close the port when the UI freezes.
public Form1()
{
mythread = new Thread(ReadFct);
myPort = new SerialPort("COM3", 9600);
myPort.ReadTimeout = 3500;
InitializeComponent();
foreach (var t in Constants.ComboParameters)
this.paramCombo.Items.Add(t);
radioClose.CheckedChanged += new EventHandler(radioButtonCheckedChanged);
radioOpen.CheckedChanged += new EventHandler(radioButtonCheckedChanged);
}
Below is the function attached to the thread
void ReadFct()
{
string aux = "";
while (readCondition)
{
if (myPort.IsOpen)
aux = myPort.ReadLine();
this.SetText(aux);
}
}
Below is the radio button event handler
public void radioButtonCheckedChanged(object sender,EventArgs e)
{
if (radioOpen.Checked && !myPort.IsOpen)
try
{
myPort.Open();
mythread.Start();
}
catch (Exception)
{
MessageBox.Show("Nu s-a putut deschide port-ul");
}
if (radioClose.Checked && myPort.IsOpen)
{
readCondition = false;
mythread.Join();
myPort.Close();
// myPort.DataReceived -= DataReceivedHandler;
}
}
The read button function:
private void readbtn_Click(object sender, EventArgs e)
{
if (!myPort.IsOpen)
MessageBox.Show("PORT NOT OPENED!");
else
{
// myPort.DataReceived += new SerialDataReceivedEventHandler(DataReceivedHandler);
readCondition = true;
if (!mythread.IsAlive)
{
mythread = new Thread(ReadFct);
mythread.Start();
}
}
I have used what MSDN suggest when changing control from another thread:
private void SetText(string text)
{
if (this.textBox1.InvokeRequired)
{
StringTb del = new StringTb(SetText);
this.Invoke(del, new object[] { text });
}
else
SetData = text;
}
It's hard to know exactly what you need, lacking a good Minimal, Complete, and Verifiable code example to illustrate the question. That said, the issue here is that the Thread.Join() method causes that thread to stop doing any other work, and the thread you use to call that method is the thread that handles all of the user interface. Worse, if your port never receives another newline, the thread you're waiting on will never terminate, because you're stuck waiting on the ReadLine() method. Even worse, even if you do get a newline, if that happens while you're stuck waiting on the Thread.Join(), the call to Invoke() will deadlock, because it needs the UI thread to do its work, and the Thread.Join() call is preventing it from getting the UI thread.
In other words, your code has multiple problems, any one of which could cause problems, but all of which together mean it just can't possibly work.
There are a variety of strategies to fix this, but IMHO the best is to use await. The first step in doing that is to change your I/O handling so that it's done asynchronously instead of dedicating a thread to it:
// Ideally, you should rename this method to "ReadFctAsync". I am leaving
// all names intact for the same of the example though.
async Task ReadFct()
{
string aux = "";
using (StreamReader reader = new StreamReader(myPort.BaseStream))
{
while (true)
{
aux = await reader.ReadLineAsync();
// This will automatically work, because the "await" will automatically
// resume the method execution in the UI thread where you need it.
this.SetText(aux);
}
}
}
Then, instead of creating a thread explicitly, just create a Task object by calling the above:
public Form1()
{
// In this approach, you can get rid of the "mythread" field altogether
myPort = new SerialPort("COM3", 9600);
myPort.ReadTimeout = 3500;
InitializeComponent();
foreach (var t in Constants.ComboParameters)
this.paramCombo.Items.Add(t);
radioClose.CheckedChanged += new EventHandler(radioButtonCheckedChanged);
radioOpen.CheckedChanged += new EventHandler(radioButtonCheckedChanged);
}
public async void radioButtonCheckedChanged(object sender,EventArgs e)
{
if (radioOpen.Checked && !myPort.IsOpen)
{
try
{
myPort.Open();
await ReadFct();
// Execution of this method will resume after the ReadFct() task
// has completed. Which it will do only on throwing an exception.
// This code doesn't have any continuation after the "await", except
// to handle that exception.
}
catch (Exception)
{
// This block will catch the exception thrown when the port is
// closed. NOTE: you should not catch "Exception". Figure out what
// *specific* exceptions you expect to happen and which you can
// handle gracefully. Any other exception can mean big trouble,
// and doing anything other than logging and terminating the process
// can lead to data corruption or other undesirable behavior from
// the program.
MessageBox.Show("Nu s-a putut deschide port-ul");
}
// Return here. We don't want the rest of the code executing after the
// continuation, because the radio button state might have changed
// by then, and we really only want this call to do work for the button
// that was selected when the method was first called. Note that it
// is probably even better if you just break this into two different
// event handlers, one for each button that might be checked.
return;
}
if (radioClose.Checked && myPort.IsOpen)
{
// Closing the port should cause `ReadLineAsync()` to throw an
// exception, which will terminate the read loop and the ReadFct()
// task
myPort.Close();
}
}
In the above, I have completely ignored the readbtn_Click() method. Lacking a good MCVE, it's not clear what role that button plays in the overall scheme. You seem to have a radio button group (of two buttons) that control whether the port is open or closed. It is not clear why then you have an additional regular button that is seemingly able to also open the port and start reading, independently of the radio group.
If you want that extra button, it seems to me that all it ought to do is change the radio group state, by checking the "open" radio button. Then let the radio group buttons handle the port state and reading. If you need more specific advice as to how to fully integrate my code example above with your entire UI, you will need to provide more detail, preferably in a new question. That new question must include a good MCVE.

How to safely show messagebox in multi-thread application?

I have something doing background and I want to show a messagebox if something wrong happens.
First I tried
var _timer = new System.Threading.Timer((o) =>
{
if(!DoCheck()){
Messagebox.Show("The message");
}
});
Nothing wrong happens.
And I have another job to be done in background, and it's invoked by button click, like
private void button3_Click(object sender, EventArgs e)
{
var task = new Task(() =>
{
DoWork();
Messagebox.Show("Done");
});
_task.Start();
}
A System.Reflection.TargetInvocationException is thrown when the MessageBox is shown.
I have also tried this.Invoke, it raised an exception, too.
My question is:
Is the first case safe?
How to make the second case work?
No. You should preferably be using System.Windows.Forms.Timer in a WinForms application. The documentation specifically calls this out:
This Windows timer is designed for a single-threaded environment where UI threads are used to perform processing. It requires that the user code have a UI message pump available and always operate from the same thread, or marshal the call onto another thread.
Furthermore, it depends on what your DoCheck method is doing. We will need to see the code of that method.
Use the BeginInvoke method:
var form = this;
var task = new Task(() =>
{
DoWork();
form.BeginInvoke(() =>
{
MessageBox.Show("Done");
});
});

ManualResetEvent wait doesn't release after being set

I'm downloading two JSON files from the webs, after which I want to allow loading two pages, but not before. However, the ManualResetEvent that is required to be set in order to load the page never "fires". Even though I know that it gets set, WaitOne never returns.
Method that launches the downloads:
private void Application_Launching(object sender, LaunchingEventArgs e)
{
PhoneApplicationService.Current.State["doneList"] = new List<int>();
PhoneApplicationService.Current.State["manualResetEvent"] = new ManualResetEvent(false);
Helpers.DownloadAndStoreJsonObject<ArticleList>("http://arkad.tlth.se/api/get_posts/", "articleList");
Helpers.DownloadAndStoreJsonObject<CompanyList>("http://arkad.tlth.se/api/get_posts/?postType=webbkatalog", "catalog");
}
The downloading method, that sets the ManualResetEvent
public static void DownloadAndStoreJsonObject<T>(string url, string objName)
{
var webClient = new WebClient();
webClient.DownloadStringCompleted += (sender, e) =>
{
if (!string.IsNullOrEmpty(e.Result))
{
var obj = ProcessJson<T>(e.Result);
PhoneApplicationService.Current.State[objName] = obj;
var doneList = PhoneApplicationService.Current.State["doneList"] as List<int>;
doneList.Add(0);
if (doneList.Count == 2) // Two items loaded
{
(PhoneApplicationService.Current.State["manualResetEvent"] as ManualResetEvent).Set(); // Signal that it's done
}
}
};
webClient.DownloadStringAsync(new Uri(url));
}
The waiting method (constructor in this case)
public SenastePage()
{
InitializeComponent();
if ((PhoneApplicationService.Current.State["doneList"] as List<int>).Count < 2)
{
(PhoneApplicationService.Current.State["manualResetEvent"] as ManualResetEvent).WaitOne();
}
SenasteArticleList.ItemsSource = (PhoneApplicationService.Current.State["articleList"] as ArticleList).posts;
}
If I wait before trying to access that constructor, it easily passes the if-statement and doesn't get caught in the WaitOne, but if I call it immediately, I get stuck, and it never returns...
Any ideas?
Blocking the UI thread must be prevented at all costs. Especially when downloading data: don't forget that your application is executing on a phone, which has a very instable network. If the data takes two minutes to load, then the UI will be freezed for two minutes. It would be an awful user experience.
There's many ways to prevent that. For instance, you can keep the same logic but waiting in a background thread instead of the UI thread:
public SenastePage()
{
// Write the XAML of your page to display the loading animation per default
InitializeComponent();
Task.Factory.StartNew(LoadData);
}
private void LoadData()
{
((ManualResetEvent)PhoneApplicationService.Current.State["manualResetEvent"]).WaitOne();
Dispatcher.BeginInvoke(() =>
{
SenasteArticleList.ItemsSource = ((ArticleList)PhoneApplicationService.Current.State["articleList"]).posts;
// Hide the loading animation
}
}
That's just a quick and dirty way to reach the result you want. You could also rewrite your code using tasks, and using Task.WhenAll to trigger an action when they're all finished.
Perhaps there is a logic problem. In the SenastePage() constructor you are waiting for the set event only if the doneList count is less than two. However, you don't fire the set event until the doneList count is equal to two. You are listening for the set event before it can ever fire.

Backgroundworker is always busy

I'm new to using event handlers and backgroundworkers, so I may be missing something completely obvious here. Still, I've been trying to fix this for two days, so I thought I might as well see what anyone had to say.
I have a backgroundworker called SqlExpressDownloader. It starts running at the beginning of my program, the rest of the work runs, and then it should wait for the operations in the SqlExpressDownloader_DoWork() method to complete before continuing. The only problem is that for some reason whenever I do while(SqlExpressDownloader.IsBusy), it always responds as busy and therefore will wait forever.
The code for the event handler is here:
private void SqlExpressDownloader_DoWork(object sender, DoWorkEventArgs e)
{
string sSource = string.Format("{0}\\{1}", Paths.Settings_Common, "sqlexpr_x64_enu.exe");
Debug.WriteLine(sSource);
Debug.WriteLine("http://www.elexioamp.com/Install/redistributables/sql2008r2express/sqlexpr_x64_enu.exe");
if (!System.IO.File.Exists(sSource))
{
WebClient oWebClient = new WebClient();
oWebClient.DownloadProgressChanged += DownloadProgressChanged;
oWebClient.DownloadDataCompleted += DownloadComplete;
oWebClient.DownloadFileAsync(new System.Uri("http://www.elexioamp.com/Install/redistributables/sql2008r2express/sqlexpr_x64_enu.exe"), sSource);
while (oWebClient.IsBusy)
{
Thread.Sleep(100);
}
e.Result = "";
DownloadFinished = true;
}
}
I have watched the code and have watched it complete this method. I even added a return after the DownloadFinished = true, but it still responds as busy. What I want to know is how to make the backgroundworker respond as not busy.
EDIT
The events are all added in the constructor as shown here:
SqlExpressDownloader = new BackgroundWorker();
SqlExpressDownloader.DoWork += new DoWorkEventHandler(this.SqlExpressDownloader_DoWork);
SqlExpressDownloader.RunWorkerCompleted += new RunWorkerCompletedEventHandler(this.SqlExpressDownloader_RunWorkerCompleted);
The RunWorkerCompleteEventHandler looks like this:
private void SqlExpressDownloader_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (e.Error != null)
{
Debug.WriteLine("The actions are complete.");
}
else
{
Debug.WriteLine("Error in completed work.");
}
}
But, when I debugged it last, it didn't actually trigger.
Instead of querying SqlExpressDownloader.IsBusy in a loop, try subscribing to the RunWorkerCompleted event of the BackgroundWorker and place your code in there that should only occur after the DoWork event has completed.
You'll also have access to the RunWorkerCompletedEventArgs, which you can check to make sure no error was thrown from the DoWork portion of your BackgroundWorker.
...
...
SqlExpressDownloader.RunWorkerCompleted += SqlExpressDownloader_RunWorkerCompleted;
SqlExpressDownloader.RunWorkerAsync();
}
private void SqlExpressDownloader_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (e.Error != null)
{
// do something in response to the error
}
// stuff to do after DoWork has completed
}
I found Joe Albahari's tutorial helpful when I was learning how to use these.
You can replace your code with more elegant async/await solution like this
private async Task SqlExpressDownloadAsync()
{
string sSource = string.Format("{0}\\{1}", Paths.Settings_Common, "sqlexpr_x64_enu.exe");
Debug.WriteLine(sSource);
Debug.WriteLine("http://www.elexioamp.com/Install/redistributables/sql2008r2express/sqlexpr_x64_enu.exe");
if (!System.IO.File.Exists(sSource))
{
WebClient oWebClient = new WebClient();
oWebClient.DownloadProgressChanged += DownloadProgressChanged;
oWebClient.DownloadDataCompleted += DownloadComplete;
await oWebClient.DownloadFileTaskAsync(new System.Uri("http://www.elexioamp.com/Install/redistributables/sql2008r2express/sqlexpr_x64_enu.exe"), sSource);
}
}
I had a similar issue. DownloadASync would fire but .IsBusy would always stay on true.
This probably won't be a common problem, just thought I share my resolution.
I used
MessageBox.Show(new Form() { TopMost = true }, "", "")
This was the cause. I also tried:
var t = new Form() { TopMost = true };
MessageBox.Show(t, "", "");
t.Dispose();
This caused the same issue.
My code had multiple threads, I assume one of them must have gotten stuck, or perhaps the MessageBox(the new Form() { TopMost = true; } ) call created a stuck thread.
As soon as I removed that part, eg.
MessageBox.Show("", "");
Everything worked as expected again.
So maybe you are creating another thread somewhere that is causing your issue.

How to block until an event is fired in c#

After asking this question, I am wondering if it is possible to wait for an event to be fired, and then get the event data and return part of it. Sort of like this:
private event MyEventHandler event;
public string ReadLine(){ return event.waitForValue().Message; }
...
event("My String");
...elsewhere...
var resp = ReadLine();
Please make sure whatever solution you provide returns the value directly rather than getting it from something else. I'm asking if the method above is available in some way. I know about Auto/ManuelResetEvent, but I don't know that they return the value directly like I did above.
Update: I declared an event using MyEventHandler (which contains a Message field). I have a method in another thread called ReadLine waiting for the event to fire. When the event fires the WaitForValue method (part of the event handling scene) returns the event args, which contains the message. The message is then returned by ReadLine to whatever had called it.
The accepted answer to that question I asked was what I did, but it just doesn't feel quite right. It almost feels like something could happen to the data between the ManuelResetEvent firing and the program retrieving the data and returning it.
Update: The main problem with the Auto/ManualResetEvent is that it is too vulnerable. A thread could wait for the event, and then not give enough time for anyone else to get it before changing it to something else. Is there a way to use locks or something else? Maybe using get and set statements.
If the current method is async then you can use TaskCompletionSource. Create a field that the event handler and the current method can access.
TaskCompletionSource<bool> tcs = null;
private async void Button_Click(object sender, RoutedEventArgs e)
{
tcs = new TaskCompletionSource<bool>();
await tcs.Task;
WelcomeTitle.Text = "Finished work";
}
private void Button_Click2(object sender, RoutedEventArgs e)
{
tcs?.TrySetResult(true);
}
This example uses a form that has a textblock named WelcomeTitle and two buttons. When the first button is clicked it starts the click event but stops at the await line. When the second button is clicked the task is completed and the WelcomeTitle text is updated. If you want to timeout as well then change
await tcs.Task;
to
await Task.WhenAny(tcs.Task, Task.Delay(25000));
if (tcs.Task.IsCompleted)
WelcomeTitle.Text = "Task Completed";
else
WelcomeTitle.Text = "Task Timed Out";
You can use ManualResetEvent. Reset the event before you fire secondary thread and then use the WaitOne() method to block the current thread. You can then have secondary thread set the ManualResetEvent which would cause the main thread to continue. Something like this:
ManualResetEvent oSignalEvent = new ManualResetEvent(false);
void SecondThread(){
//DoStuff
oSignalEvent.Set();
}
void Main(){
//DoStuff
//Call second thread
System.Threading.Thread oSecondThread = new System.Threading.Thread(SecondThread);
oSecondThread.Start();
oSignalEvent.WaitOne(); //This thread will block here until the reset event is sent.
oSignalEvent.Reset();
//Do more stuff
}
A very easy kind of event you can wait for is the ManualResetEvent, and even better, the ManualResetEventSlim.
They have a WaitOne() method that does exactly that. You can wait forever, or set a timeout, or a "cancellation token" which is a way for you to decide to stop waiting for the event (if you want to cancel your work, or your app is asked to exit).
You fire them calling Set().
Here is the doc.
If you're happy to use the Microsoft Reactive Extensions, then this can work nicely:
public class Foo
{
public delegate void MyEventHandler(object source, MessageEventArgs args);
public event MyEventHandler _event;
public string ReadLine()
{
return Observable
.FromEventPattern<MyEventHandler, MessageEventArgs>(
h => this._event += h,
h => this._event -= h)
.Select(ep => ep.EventArgs.Message)
.First();
}
public void SendLine(string message)
{
_event(this, new MessageEventArgs() { Message = message });
}
}
public class MessageEventArgs : EventArgs
{
public string Message;
}
I can use it like this:
var foo = new Foo();
ThreadPoolScheduler.Instance
.Schedule(
TimeSpan.FromSeconds(5.0),
() => foo.SendLine("Bar!"));
var resp = foo.ReadLine();
Console.WriteLine(resp);
I needed to call the SendLine message on a different thread to avoid locking, but this code shows that it works as expected.
Try it : e.Handled = true; It works to prevent KeyEventArgs, for example.

Categories