I am a newbie to events and I am asking myself if a event that gets triggered (from a SDK) is executed on the main thread or async on another.
Can anyone help me how to check this?
This for example gets executed if the event fires and it doesn't block the UI, which surprises me (triggered max. speed each 0.05 seconds):
void TrackingOnNewTrackingObservations(object sender, TrackingEventArgs e)
{
try
{
dgvTracking.Rows[0].Cells["cName"].Value = "SlopeDistance";
dgvTracking.Rows[0].Cells["cValue"].Value = string.Format("{0:0.00} m", e.SlopeDistance);
dgvTracking.Rows[1].Cells["cName"].Value = "HorizontalAngle";
dgvTracking.Rows[1].Cells["cValue"].Value = string.Format("{0:0.00000}°", e.HorizontalAngle);
dgvTracking.Rows[2].Cells["cName"].Value = "VerticalAngle";
dgvTracking.Rows[2].Cells["cValue"].Value = string.Format("{0:0.00000}°", e.VerticalAngle);
}
catch (Exception ex)
{
Debug.WriteLine("Error TrackingOnNewTrackingObservations ->" + ex.Message);
}
Use Corewindow.Dispatcher.HasThreadAccess to test whether or not the execution is on the UI thread.
Normally events are going to be invoked on the same thread as the invocation of the method that led to the event. If the class is a UI class them this is going to be the UI thread.
Related
I created Backgroundworker to operate on serial port which uses some methods provided by different classes. I'd like to cancel a worker each time an exception from those methods occurs.
Actually, I have some idea how to do it, using events, but I am afraid it's not optimal solution or a good practice. In that case I should firing event in each method, which is not efficient in my opinion.
For ex., one from methods, ReceiveRecord looks like that:
public string ReceiveRecord()
{
try
{
var receivedLine = _serial.ReadLine();
return receivedLine;
}
catch (TimeoutException exception)
{
MessageBox.Show($"Error was occured: \r\n {exception.Message}", "Timeout error",
MessageBoxButton.OK, MessageBoxImage.Error);
// HERE i want to fire worker cancellation
}
return String.Empty;
}
Worker calls that methods periodically.
I considered returning false, when errors occurs, but I don't have ANY idea, how to do it, if method returns string...
Are there any simple method to fire DoWork cancellation from the catch fragment of code?
Don't catch the exception in the method. Let the caller handle it in BackgroundWorker's RunWorkerCompleted event.
See Background worker exception handling
The BackgroundWorker class has a WorkerSupportsCancellation boolean which tells the worker whether the worker supports cancellation or not. Enable this then in your backgroundworker DoWork event, check if CancellationPending, if true, end the thread, otherwise continue with normal operation.
Use the CancelAsync() function to set cancellation pending true. You would want to set this in your catch block.
In my example below, my background worker is in a while loop, but it should give you the right idea.
private void bw_DoWork(object sender, DoWorkEventArgs e)
{
try
{
while (true)
{
if (bw.CancellationPending)
break;
do_state_machine();
Thread.Sleep(100);
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
I need some help. I started c# and not very familiar with event handling and threading yet. As a beginner and as time and exposure progresses, I would like to learn more on these advanced topics and improved and hope all of you here can help me.
I ran onto this problem of "Cross-thread operation not valid: Control 'textbox control called stackStatus' accessed from a thread other than the thread it was created on". I have tried to troubleshoot this whole day but simply no avail. I am stuck. :-( The program hits an exception and cannot continue to execute smoothly.
I have read the following threads and tried a few things but I guess I am still missing something. Appreciate if someone can help me out here. Thanks.
Cross-thread operation not valid: Control accessed from a thread other than the thread it was created on
Cross-thread operation not valid: Control 'textBox1' accessed from a thread other than the thread it was created on
Here's are most of the portion of the code:
private void createCloud_Click(object sender, EventArgs e)
{
CreateCloud(); //start creation method
stackStatus.Text = "Creating stack..."; //updates the cloud status textbox
stackStatus.Refresh();
Cursor.Current = Cursors.WaitCursor; //change the cursor to wait state
Start_Describestack(); //call describe method to find out the status of cloud creation progress
Task.Delay(12000); // wait 12s in case not ready
Start_Describestack(); // call again describe method to find out the cloud creation progress status
Cursor.Current = Cursors.Default; //put cursor on wait
describeevents(); // call method to get all cloud creation event data and publish on the datagridview
}
private void Start_Describestack()
{
//method making use of timer to call
_timer = new System.Timers.Timer(15000);
_timer.Elapsed += new ElapsedEventHandler(describeStack);
_timer.Enabled = true;
}
delegate void describeStackCallBack(object sender, ElapsedEventArgs e);
private void describeStack(object sender, ElapsedEventArgs e)
{
//this method makes api calls through cloudclient to describe the stack
//this is where the "Cross-thread operation not valid: Control 'stackStatus' accessed from a thread other than the thread it was created on"
var client = new cloudclient();
var request2 = new StacksRequest();
request2.Cloudstackname = stackid;
try
{
var response = client.DescribeCloudStacks(request2);
foreach (var stack in response.Stacks)
{
//something is wrong here but I do not know how to fix it. Please help
if (this.stackStatus.InvokeRequired)
{
describeStackCallBack d = new describeStackCallBack(describeStack);
this.Invoke(d, new object[] { sender, e });
stackStatus.Refresh();
describevents();
}
else
{
stackStatus.Text = stack.StackStatus;
stackStatus.Refresh();
describeevents();
}
}
}
catch (Exception)
{
if (this.stackStatus.InvokeRequired)
{
describeStackCallBack d = new describeStackCallBack(describeStack);
this.Invoke(d, new object[] { sender, e });
stackStatus.Text = "Stack not found/Deleted";
}
else
{ stackStatus.Text = "Stack not found/Deleted"; }
}
describeevents();
}
private void describeevents()
{
var newclient = new cloudclient();
var request3 = new eventrequest();
request3.Cloudstackname = stackid;
try
{
var response = newclient.eventstack(request3);
dataGridView3.Rows.Clear();
foreach (var events in response.sevents)
{
dataGridView3.Rows.Add(events.Timestamp, events.ResourceStatus, events.ResourceType);
}
}
catch (Exception)
{
dataGridView3.Rows.Clear();
MessageBox.Show("Stack not ready!");
}
dataGridView3.Refresh();
}
Rather than doing :
stackStatus.Text = "some text";
Try :
stackStatus.Invoke((Action)delegate
{
stackStatus.Text = "some text";
});
Note that GUI element assignment outside the thread or they are declared is deprecated because the controls may no longer be available at any time.
There are two issues in your approach, which conspire to prevent your attempt to imitate the solution to the exception from working:
You have failed to note that the proposed solution calls itself, and in so doing, causes the foreach to be restarted for each time it's invoked from the worker thread.
You are following Microsoft canonical implementation of cross-thread-friendly Invoke()-based code, which IMHO is lame.
It is my opinion that there is no point in ever checking InvokeRequired. The standard pattern always involves situations where on the first entry, you know you will require Invoke(), and even if you didn't, there's no real harm in calling Invoke() when it's not necessary.
Instead, you should always keep separate the code that should run in the UI thread, and the code that does not. Then, in the code that does not, always use Invoke() to execute the code that does.
For example:
private void Start_Describestack()
{
//method making use of timer to call
_timer = new System.Timers.Timer(15000);
_timer.Elapsed += new ElapsedEventHandler(_timer_Elapsed);
_timer.Enabled = true;
}
private void _timer_Elapsed(object sender, ElapsedEventArgs e)
{
Invoke((MethodInvoker)describeStack);
}
private void describeStack()
{
var client = new cloudclient();
var request2 = new StacksRequest();
request2.Cloudstackname = stackid;
try
{
var response = client.DescribeCloudStacks(request2);
foreach (var stack in response.Stacks)
{
stackStatus.Text = stack.StackStatus;
stackStatus.Refresh();
describeevents();
}
}
catch (Exception)
{
stackStatus.Text = "Stack not found/Deleted";
}
describeevents();
}
That said, an improvement on the above would be to use System.Windows.Forms.Timer instead of System.Timers.Timer. The latter raises the Elapsed event on a worker thread, but the former raises its event on the UI thread, right where you want it. No Invoke() required at all.
You have at least one other problem with your code as well:
private void createCloud_Click(object sender, EventArgs e)
{
CreateCloud(); //start creation method
stackStatus.Text = "Creating stack..."; //updates the cloud status textbox
stackStatus.Refresh();
Cursor.Current = Cursors.WaitCursor; //change the cursor to wait state
Start_Describestack(); //call describe method to find out the status of cloud creation progress
Task.Delay(12000); // wait 12s in case not ready
Start_Describestack(); // call again describe method to find out the cloud creation progress status
Cursor.Current = Cursors.Default; //put cursor on wait
describeevents(); // call method to get all cloud creation event data and publish on the datagridview
}
In the above, the call to Task.Delay(12000); accomplishes nothing. The Task.Delay() method doesn't actually block the current thread. Instead, it returns an awaitable task object. The code in which it appears only is delayed if you wait on the returned object.
It's also questionable to call Start_Describestack() twice, because this method doesn't do anything except start the timer. Calling it twice means now you have two timers running.
Finally, you should also not have all those calls to Refresh() in your code. Correctly written Windows Forms code will not need anything like that. Updates to control properties will cause control invalidation automatically, and the control will update as needed at its next opportunity, which as long as the code is written correctly, will be soon enough for the user to not notice any significant delay.
Now, putting all of the above together, it seems to me that you should avoid using the timer altogether. There is still the potential problem that your call to DescribeCloudStacks() is a lengthy one, and could cause the UI to momentarily appear "stuck", which obviously isn't a desirable thing. In addition, the timer-based code, whether you require Invoke() or not, can be harder to understand, especially for someone new to asynchronous programming and threading.
Using the async/await feature, you can write the code in a conventional, procedural way, while still ensuring that the UI remains responsive, and that the UI-related code is always executed in the UI thread where it belongs. That might look something like this:
private async void createCloud_Click(object sender, EventArgs e)
{
CreateCloud(); //start creation method
stackStatus.Text = "Creating stack..."; //updates the cloud status textbox
Cursor.Current = Cursors.WaitCursor; //change the cursor to wait state
await describeStack(); //call describe method to find out the status of cloud creation progress
await Task.Delay(12000); // wait 12s in case not ready
await describeStack(); // call again describe method to find out the cloud creation progress status
Cursor.Current = Cursors.Default; //put cursor on wait
describeevents(); // call method to get all cloud creation event data and publish on the datagridview
}
private async Task describeStack()
{
var client = new cloudclient();
var request2 = new StacksRequest();
request2.Cloudstackname = stackid;
try
{
var response = await Task.Run(() => client.DescribeCloudStacks(request2));
foreach (var stack in response.Stacks)
{
stackStatus.Text = stack.StackStatus;
describeevents();
}
}
catch (Exception)
{
stackStatus.Text = "Stack not found/Deleted";
}
describeevents();
}
The above executes most of the describeStacks() method in the UI thread. The exception would be the DescribeCloudStacks() method call, which is run as a worker task. While it's running, the UI thread is free to operate normally. Execution of the describeStacks() method is temporarily put "on hold" (without blocking the UI thread) while the worker task runs, and then is resumed when it completes.
It's not clear from your original example whether you really wanted a repeating timer or not. The above doesn't use any loops; it calls the describeStack() method only twice, with a 12-second delay in between. But if you want a loop, you can do that as well. Just use the await Task.Delay() for the delay and await describeStack() for the operation, and put that in a loop as you like.
I don't see where the stackStatus object is created so I'm just guessing that you are creating it through a contructor for the class containing describeStack() and then you are registering an event handler for the click. I think what is happening is the event handler is being run on a different thread from the one in which the instance was created so you might have to change how you create the stackStatus object. That error is likely happening because whatever type the stackStatus was created from is known to not be reentrant so when the runtime detects access between threads it raises an exception so you are aware and can either prevent or recover from race-conditions or deadlocks.
I have been looking all over the web at examples like this:
https://msdn.microsoft.com/en-us/library/edzehd2t%28v=vs.110%29.aspx
and all over stackoverflow but I cannot get this to work or find the answer. Or the answer doesn't apply to what I am doing, or there is not enough information in the answer for me to understand how to make this work.
I'm trying to write good code here and avoid Application.DoEvents(). Take a look at the code.
private void btnSendQ_Click(object sender, EventArgs e)
{
//launch a thread from a button click event
Thread ManualQueryThread = new Thread(StartManualQueryThread_Thread);
ManualQueryThread.Priority = ThreadPriority.Normal;
ManualQueryThread.IsBackground = true;
Platform.Log(LogLevel.Debug, "Starting Manual Query Thread.");
Stats.g_CurrentStatus = "Starting Manual Query Thread.";
ManualQueryThread.Start();
}
private void StartManualQueryThread_Thread()
{
try
{
//Here work is performed in the background thread populating objects with data.
}
catch (Exception e)
{
Platform.Log(LogLevel.Error, "Error in StartManualQueryThread(). The error is: " + e.ToString());
}
finally
{
//raise event here since the thread has completed
OnC_FindComplete(EventArgs.Empty);
}
}
public event EventHandler C_FindComplete;
protected virtual void OnC_FindComplete(EventArgs e)
{
//display the gathered information in a datagrid using DisplayC_FindResults(), and enable UI objects.
EventHandler handler = C_FindComplete;
DisplayC_FindResults();
btnSendQ.Enabled = true;
btnMove.Enabled = true;
}
The problem is when a release build is created the datagrid gets a null reference exception and does the red X thing because it is still being updated by a background thread. So how do I properly raise an event in a background thread, and process that event in the UI thread to interact with the UI?
Try to use BackgroundWorker. You'll be able to update your UI in RunWorkerCompleted event.
I am having trouble with multi-threading and event delegate in C#. If anyone could help me solve this problem, that would be great. The problem is with multiple threads and events. In a single thread or up to 10 threads, custom event is triggered properly and works fine. However, when I increase the number of threads to 15 or 20, event are not triggered at all. Here is sample piece of code:
LegacyMemberStream memberStream=new LegacyMemberStream();
memberStream.OpenStream();
legacyMemberStrm = (LegacyMemberStream)memberStream;
legacyMemberStrm.ThreadErrorOccur += OnParserThreadInterrupt;
Here is code for OnParserThreadInterrupt():
private void OnParserThreadInterrupt(Object Sender, ThreadErrorEventArgs args)
{
// Exception logging is done here
}
And, the part of LegacyMemberStream.OpenStream() method is:
parserThreads[i].OnThreadError = HandleThreadError;
parserThreads[i].StartThread();
This method simply initializes number of threads requested and assigns event for each thread when exception occur and finally starts threads.
And, HandleThreadError method in LegacyMemberStream is :
public void HandleThreadError(Exception exception, string threadName)
{
lock (SyncObject)
{
Console.WriteLine("From parser thread");
for (int i = 0; i < parserThreads.Length; i++)
{
if (parserThreads[i].Name.Equals(threadName))
{
parserThreads[i].StopThread();
break;
}
}
int threadFailureErrorCode = -1111;
OnThreadFailure(new ThreadErrorEventArgs(threadFailureErrorCode, true,exception));
somethingQueue.StopQueuing();
}
}
LegacyMemberStream.OnThreadFailure:
protected virtual void OnThreadFailure(ThreadErrorEventArgs e)
{
lock (_locker)
{
var threaderrorOccur = ThreadErrorOccur;
// Console.WriteLine("Exception occurred");
if (threaderrorOccur != null)
{
ThreadErrorOccur(this, e);
}
}
}
For any number of threads, HandleThreadError() method is called from OnThreadError event.
What I have discovered so far from debugging is that, OnParserThreadInterrupt() method is not being invoked when number of threads are greater than 15 (or sometimes 20). However, for same input and same scenario, OnParserThreadInterrupt() event is triggered when number of threads is lesser. I can't understand why event is not being triggered when number of threads are increased.
Basing on this code that you have shared, the only reason that seems to be possible is that the error happens before you submit the event handler. so just change order of the first lines to be:
LegacyMemberStream memberStream=new LegacyMemberStream();
legacyMemberStrm = (LegacyMemberStream)memberStream;
legacyMemberStrm.ThreadErrorOccur += OnParserThreadInterrupt;
memberStream.OpenStream();
If the context switch was before you got the chance to submit the event handler then this function:
protected virtual void OnThreadFailure(ThreadErrorEventArgs e)
{
lock (_locker)
{
var threaderrorOccur = ThreadErrorOccur;
// Console.WriteLine("Exception occurred");
if (threaderrorOccur != null)
{
ThreadErrorOccur(this, e);
}
}
}
skipped the call to ThreadErrorOccur
because the if statement is false.
Why is that related to number of threads? I think it is matter of probability. Maybe creating many threads consumes enought time so the main thread context switched, then the threads run (also context switched between them), get errors... and all of that happens before the main thread, whci creates them had the chance to do the line that subscribes the ThreadErrorOccur event
Hope it solves your issue.
I want to know what can be problem if i have the following POC...
public void DoProcess() // called as Do_Work
{
textUpdater = null;
try
{
SetButtonEnabled(false);
aHandler = new DataHandler();
aHandler.Initialize(_configuration);
aHandler.GetDataFromWebAndSave();
MessageBox.Show("completed");
}
catch (Exception ex)
{
MessageBox.Show(ex.Message + " \r\n\r\n" + ex.StackTrace);
}
SetButtonEnabled(true);
}
Usually a backgroundWorker process includes a loop where one can easily see whether cancel is called or not. In my case, i can't check ...
Say I CLOSE the windows form (containing above code). Will everything be terminated safely ??
If not, then how can i do it ?
(i guess other way could be 'use thread').
The thread proc of your backgroundworker doesn't have to have a loop. And it doesn't have to be cancelled to finish. It simply finishes when... the proc exits!
When it finishes, RunWorkerCompleted will be called.
I see a major problem in your call though: You manipulate your GUI from the background thread. This is a no-no! All manipulations of a GUI element must be made from the thread that created the element. In your case, use ReportProgress() to delegate status information to the ProgressChanged handler that will execute it in the foreground thread.
In addition, as Rewinder wrote, you can cancel the worker from FormClosing(). But if you never monitor CancellationPending from your worker proc, this is pointless.
If you want to be sure your backgroundworker is cancelled, you can do something like this:
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
myBackgroundWorker.CancelAsync();
}