Code not executing before thread sleep? - c#

I do this:
clear();
coinRefundComplete.Visible = true;
state = 0;
System.Threading.Thread.Sleep(4000);
clear();
greeting.Visible = true;
rate.Visible = true;
refundTicket.Visible = true;
currentTime.Visible = true;
I expect the coinRefundComplete Text (it is a label) to appear for 4 seconds, then get cleared by a method I defined with clear(), and then some other stuff happens. Instead after I clear my form with the first clear(), my form is blank for 4 seconds, then finishes properly.

Use async/await approach.
Make your method async - below example for eventhandler of button click
private async void ButtonClick(object sender, EventArgs e)
{
clear();
coinRefundComplete.Visible = true;
state = 0;
await Task.Delay(4000);
clear();
greeting.Visible = true;
rate.Visible = true;
refundTicket.Visible = true;
currentTime.Visible = true;
}
On line await Task.Delay(4000); UI thread will be release, which will update all changes were made before. After 4 seconds method will continue executing on the UI thread.

Although putting the GUI thread to sleep is never desirable but allowing the GUI thread to refresh the control state before going to Sleep will show you the changes you want. Call Control.Update or Control.Refresh after making it visible and before going to sleep so that the GUI thread is able to show changes before it goes to sleep.
clear();
coinRefundComplete.Visible = true;
label1.Update();
state = 0;
System.Threading.Thread.Sleep(4000);
clear();
You should be carefull while using Thread.Sleep, In your case it is GUI thread and GUI will be irresponsive for the time you sleep. Knowing the reason why you want to block thread could bring some other better suggestion.
Edit
You can use other thread for adding delay without blocking the GUI thread. If you can use framework 4.5 then you can use async / await construct or read this article Using async/await without .NET Framework 4.5 .
private async void testAsyncAwaitDely_Click(object sender, EventArgs e)
{
clear();
coinRefundComplete.Visible = true;
state = 0;
await Task.Delay(4000);
clear();
//Your code
}

Update [22-Apr-2018]: I've not deleted this answer so that you don't go the wrong path even though my answer is indeed a possible solution to OP's question (specially when it costs me some negative reputation as well). You should read below posts to really convince yourself as to why Application.DoEvents isn't a great solution to this problem:
Use of Application.DoEvents
Is DoEvents Evil?
Your UI is not refreshing because you are doing your entire processing on UI thread so UI thread is not getting any chance to refresh the UI elements. You need to call the Application.DoEvents() function at any place where you seek UI to be refreshed and loaded with latest changes. Here is your modified code. I've added one line of code before calling sleep on the current UI thread:
clear();
coinRefundComplete.Visible = true;
state = 0;
//new line of code
System.Windows.Forms.Application.DoEvents();
System.Threading.Thread.Sleep(4000);
clear();
greeting.Visible = true;
rate.Visible = true;
refundTicket.Visible = true;
currentTime.Visible = true;

Related

C# UWP XAML- Updating controls "synchronously"

Throughout this question, I've included some links which show that I've done some work searching for a solution.
I'm developing a UWP app with touchscreen and GPIO.
UI has a stop button, a reset button, and a textblock. GPIO is used for a physical start button, a motor, and 2 limit switches. Motor can rotate until it runs into a limit switch.
Code to control the hardware (e.g., Motor.Forward()) has been written and tested, but is excluded from this question for brevity. Code for the stop button is excluded for the same reason.
If the steps in these methods would perform synchronously... desired behavior might be described by the following code:
//Event handler for when physical start button is pushed
private async void StartButtonPin_ValueChanged(GpioPin sender, GpioPinValueChangedEventArgs args)
{
Start();
}
private void Start()
{
//Update UI
stopButton.IsEnabled = true;
resetButton.IsEnabled = false;
textBlock.Text = "Motor turning";
Motor.Forward();
while(Motor.HasNotHitEndLimit())
Motor.Off();
//Update UI
stopButton.IsEnabled = false;
resetButton.IsEnabled = true;
textBlock.Text = "Task complete";
}
//Event handler for reset button
private void btnReset_Click()
{
//Update UI
stopButton.IsEnabled = true;
resetButton.IsEnabled = false;
textBlock.Text = "Motor turning";
Motor.Back();
while(Motor.HasNotHitStartLimit())
Motor.Off();
//Update UI
stopButton.IsEnabled = false;
resetButton.IsEnabled = true;
textBlock.Text = "Reset complete";
}
If I recall correctly, UI updates within "private void btnReset_Click()" work, but they are not synchronous... I.e., all of the UI updates were completing right after "btnReset_Click()" finished.
From reading answers to similar questions... it seems that UI updates within "Start()" fail because I'm not on the UI thread ("The application called an interface that was marshalled for a different thread.").
It seems that Task Asynchronous Pattern is a common answer to these types of questions. However, my attempts to do this have yielded strange results...
The code below is the closest I've come to the desired result. I added async tasks that use CoreDispatcher to handle UI updates.
//Task for updating the textblock in the UI
private async Task UpdateText(string updateText)
{
await Dispatcher.RunAsync(CoreDispatcherPriority.Normal,
new DispatchedHandler(() => { textBlock.Text = updateText; }));
}
//Task for enable/disable a given button
private async Task UpdateButton(Button btn, bool shouldThisBeEnabled)
{
await Dispatcher.RunAsync(CoreDispatcherPriority.Normal,
new DispatchedHandler(() => { btn.IsEnabled = shouldThisBeEnabled; }));
}
//Event handler for when physical start button is pushed
private async void StartButtonPin_ValueChanged(GpioPin sender, GpioPinValueChangedEventArgs args)
{
Start();
}
private void Start()
{
//Update UI
UpdateButton(stopButton,true).Wait();
UpdateButton(resetButton,false).Wait();
UpdateText("Motor turning").Wait();
Motor.Forward();
while(Motor.HasNotHitEndLimit())
Task.Delay(1).Wait();
Motor.Off();
//Update UI
UpdateButton(stopButton,false).Wait();
UpdateButton(resetButton,true).Wait();
UpdateText("Task complete").Wait();
}
//Event handler for reset button
private async void btnReset_Click()
{
//Update UI
await UpdateButton(stopButton,true);
await UpdateButton(resetButton,false);
await UpdateText("Motor turning");
await Task.Delay(1);
Motor.Back();
while(Motor.HasNotHitStartLimit())
await Task.Delay(1);
Motor.Off();
//Update UI
await UpdateButton(stopButton,false);
await UpdateButton(resetButton,true);
await UpdateText("Reset complete");
}
Problems/idiosyncrasies with the code above (besides any beginner mistakes I might be making due to just starting out with C#... and the fact that it seems overly complicated and confusing):
-In "Start()" I use .Wait() on the tasks (because it seems to work, I don't really understand why...), and in btnReset_Click() it worked best to await them...
-btnReset_Click() is not synchronous. UI updates appear to be "one step behind"... I.e., in debug mode, the stop button enables when I step over "await UpdateButton(resetButton,false)", reset button disables when I step over "await UpdateText("Motor turning")", and so on.
-Regarding btnReset_Click()... The while loop lasts MUCH longer than 1 millisecond in real time, yet if I remove all "await Task.Delay(1)" then the UI updates are "one step behind". With "await Task.Delay(1)" included, the UI updates get "caught up" to where they should be. Why does "await Task.Delay(1)" affect UI updates this way?
If any knowledgeable folks are willing to address some/all of this question and maybe let me prod them for details about their answer(s), I'd be very grateful!
Bonus question.... I also have a "Toggle Test Mode" button on the touchscreen which enables one list of UI buttons and disables another (based on a static bool "testmode"). I don't need to use TAP to update the UI here, but recall that I want to do this synchronously (even though it seems pointless in this example).
private async void btnTestMode_Click(object sender, RoutedEventArgs e)
{
testMode = !testMode;
if (testMode == true)
{
await UpdateButtons(TestModeButtons,true);
await UpdateButtons(NormalModeButtons,false);
return;
}
await UpdateButtons(TestModeButtons,true);
await UpdateButtons(NormalModeButtons,false);
}
private async Task UpdateButtons(List<Button> btns, enable)
{
foreach (var btn in btns)
{
await Dispatcher.RunAsync(CoreDispatcherPriority.Normal,
new DispatchedHandler(() => { btn.IsEnabled = enable; }));
}
}
As it's written above, this behaves like btnReset_Click()... where the UI updates are "one step behind". However, if I add ".ConfigureAwait(false)" to each await in the event handler, then it becomes synchronous. I've done some reading on this topic, but don't fully understand it yet, and I would love for someone with a better understanding to help me understand it as it relates to my project.
You should not be doing any of that Dispatcher.Run and similar...
First stop and think and understand what your problem is and why the UI does not update.
Create a new thread where you control your motors (separate from the UI thread).
On the button clicks, call method on your motors thread.
When there are events on the motors thread where you need to update the UI, call (synchronously?) methods on the UI thread.
In a nutshell, consider these tips as you build your app:
Never call .Wait() or Result or otherwise try to block on an asynchronous operation on a UI dispatcher thread
If you do want to create worker threads to do blocking operations, try await Task.Run(...) for its simplicity (you can create raw threads but it's more work). Same for busy-waits like while(notDone) ; // empty
From a background thread (such as one created by Task.Run), if you want to update the UI then you would use Dispatcher.RunAsync(...) but only to set the properties of your UI
To disable your UI while a background thread does work, set IsEnabled=false or add a top-most emi-transparent interaction shield etc.
Also, try starting with something simple (eg, no hardware access; just use Task.Delay(...).Wait() to simulate blocking on the hardware). Once you have the UI basically working you can plug in the hardware calls.

Cross-thread operation not valid: Control 'textbox' accessed from a thread other than the thread it was created on

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.

Is it Possible to Make a Simple C# Marquee Progress Bar without a Background Worker?

I have a very simple form with a progress bar on it and a delete button. When the user clicks "Delete", a stored procedure runs for around five minutes.
I would like a simple marquee progress bar to run but it never appears. I read all the other questions about this but they all required a background worker.
Is it possible to make a marquee progress bar without a background worker?
public partial class ProgressBarSample
{
public ProgressBarSample
{
progressBar1.Style = ProgressBarStyle.Marquee;
progressBar1.Visible = false;
}
private void btnDelete_Click(object sender, EventArgs e)
{
progressBar1.Visible = true;
// run stored procedure that takes around 5 minutes
Task.Delay(10000);
progressBar1.Visible = false;
}
}
Your code
progressBar1.Visible = true;
// run stored procedure that takes around 5 minutes
Task.Delay(10000);
progressBar1.Visible = false;
prevents windows messages from being processed by your application until the delay completes because you block the thread responsible for processing those messages. Windows relies on those messages being processed in a timely manner.
The result is that your user interface will appear unresponsive to the user.
There are a number of mechanisms that allow you to do your processing on a second thread. BackgroundWorker is one that was commonly used at the time WinForms first came out and still a solid choice. You can use any technique that does the long-running work on another thread, but you must do it on that other thread.
You can use the async pattern to simplify the coding for that other thread
private async void btnDelete_Click(object sender, EventArgs e)
{
progressBar1.Visible = true;
await Task.Run(() =>
{
// run stored procedure that takes around 5 minutes
Task.Delay(10000);
});
progressBar1.Visible = false;
}
Without a good Minimal, Complete, and Verifiable code example that reliably reproduces your problem, it's impossible to say for sure what the issue is. However, the code you posted won't work. Your btnDelete_Click() method sets the Visible property to true, but then immediately sets it back to false, because the Task.Delay() method doesn't actually block.
Probably what you want is this:
private async void btnDelete_Click(object sender, EventArgs e)
{
progressBar1.Visible = true;
// run stored procedure that takes around 5 minutes
await Task.Delay(10000);
progressBar1.Visible = false;
}
BackgroundWorker is the generally accepted method for doing such background work, hence the name; but for a primitive "show for X period of time", you could use a Timer that checks the amount of time passed since (in this case) delete was last clicked to see if it should hide the control (and disable itself, no use ticking when there is nothing to do.)
Something like:
public partial class ProgressBarSample
{
TimeSpan pbShowDuration = [blah blah];
DateTime pbShowFrom = DateTime.MinDate;
public ProgressBarSample
{
progressBar1.Style = ProgressBarStyle.Marquee;
progressBar1.Visible = false;
}
private void btnDelete_Click(object sender, EventArgs e)
{
progressBar1.Visible = true;
pbShowFrom = DateTime.Now;
timer1.Enabled = true;
// run stored procedure that takes around 5 minutes
}
private void timer1_Tick(object sender, EventArgs e)
{
if ((DateTime.Now - pbShowFrom) > pbShowDuration)
{
timer1.Enabled = false;
progressBar1.Visible = false;
}
}
}
But how are you planning to update the progress bar?
With the new task, sync, await features in C# you have a lot of options. If you don't need to do anything other than let the user know the operation is done you can start your progress bar, then start a task that runs your proc and when it's done stop the progress bar. I would personally put something other than a progress bar. To me a progress bar means you have a finite amount of time to wait. If your SP can vary in time I would go with some kind of busy display icon or something like that.
I personally would useh Task.ContinueWith in this case.
This MSDN article show a great way to handle it.
https://msdn.microsoft.com/en-us/library/dd270696(v=vs.110).aspx
Background worker is an old method which is superseded by using Tasks. Tasks have more functionality, can do things backgroundworkers cannot, and are much simpler to use.

C# Threading Run and Cancel button, need to be able to cancel long proccessing run

When a user clicks on Run, the application runs through a lot of code to generate a model and display it in a Chart. The Run takes about 1-2 minutes to run. I also have a Cancel button that gets enabled after the Run button is clicked. I am working with DotSpatial, so my buttons are on a plugin panel in a ribbon UI. The click event on the Run and Cancel start in the plugin, which calls the back-end class's code Run and Click.
When the user hits cancel after the run starts, there is a delay, but the cancel method is invokes and executes, but the run never stops and we eventually see the chart display. So, I'm thinking I need a separate thread for the Run. I'm fairly new to programming, and never worked with Threading. I've looked into it and added the below code, but my thread method isn't running. Here's my code:
The Run button is clicked:
This is at the top:
//check to see if RunModel thread needs to stop or continue
private volatile bool stopRun = false;
private Thread runThread;
Then this is the method that's called from the click event:
public void btnRun_testingThread(object sender, EventArgs e)
{
//create a new thread to run the RunModel
if (runThread == null)
{
//we don't want to stop this thread
stopRun = false;
runThread = new Thread(RunModel);
runThread.Start(); <--this isn't doing anything
}
So, I would think that when the code gets to the runThread.Start(), it would jump into my RunModel method and start running through the code. But it doesn't. Additionally, I'll want to cancel out of this thread (once I have it working correctly), so I have this, which gets called from the cancel click method:
private void StopRunThread()
{
if (runThread != null)
{
//we want to stop the thread
stopRun = true;
//gracefully pause until the thread exits
runThread.Join();
runThread = null;
}
}
Then the this is the RunModel() where I'm checking occasionally to see if the stopRun bool has changed.
public void RunModel()
{
...some code.....
//check to see if cancel was clicked
if (stopRun)
{
....clean up code....
return;
}
....some more code....
//check to see if cancel was clicked
if (stopRun)
{
....clean up code....
return;
}
}
And the cancel button click method:
public void btnCancel_Click(Object sender, EventArgs e)
{
stopRun = true;
StopRunThread();
//the model run has been canceled
....some code.....
}
Any help on getting the thread.start to actually run the Run method? Then do I need to constantly check the volatile bool in the run in order to clean everything up if it's being stopped? Thanks!
I think you'd be best looking at the BackgroundWorker - this essentially runs separately but can watch out for cancellation commands. Make sure you add 'WorkerSupportCancellation' when you initialise it:
BackgroundWorker backgroundWorker1 = new BackgroundWorker();
backgroundWorker1.DoWork += new DoWorkEventHandler(backgroundWorker1_DoWork); // This does the job ...
backgroundWorker1.WorkerSupportsCancellation = true; // This allows cancellation.
Then on click you can start your process:
public void btnRun_testingThread(object sender, EventArgs e)
{
backgroundWorker1.RunWorkerAsync();
}
Your cancel button can issue a cancellation request:
public void btnCancel_Click(Object sender, EventArgs e)
{
backgroundWorker1.CancelAsync();
}
Then your worker can monitor for this as it's doing it's work ...
void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
for (int i = 0; i < 100; i++)
{
if (backgroundWorker1.CancellationPending)
{
break;
}
else
{
// Do whatever you're doing.
}
}
e.Result = backgroundWorker1.CancellationPending ? null : orders;
}
You can enhance this further by adding progress bars etc., but that gets a bit more complicated so I won't go into it here.
Considering new info provided in commend I believe you just missed a start of the RunModel() method in debugger because of wrong assumption regarding thread.Start() method behaviour.
Please see a note from MSDN, Thread.Start Method
Once a thread is in the ThreadState.Running state, the operating
system can schedule it for execution. The thread begins executing
at the first line of the method represented by the ThreadStart or
ParameterizedThreadStart delegate supplied to the thread constructor.
Small demonstration that thread start takes some time bits, for me it starts in 38-40 milliseconds:
Stopwatch watch = new Stopwatch();
Thread thread = new Thread((ThreadStart)watch.Stop);
thread.Start();
watch.Start();
Thread.Sleep(5000);
double startedAfter = watch.ElapsedMilliseconds;
Since .NET Framework 4.0 consider using TPL Tasks rather than threads explicitly, some pros:
You can easily synchronize with UI thread by passing in a Task UI Thread synchronization context
You can easily stop a Taks using CancellationToken

Can BeginInvoke interrupt code already running on the UI thread?

Suppose I have some code which is running in the UI thread, which spawns a new thread to do work in the background, and then goes on to do UI work. When the background work is done, BeginInvoke is called from the new thread. If the UI work is still going on, will the callback given to BeginInvoke interrupt the UI work, or will it wait?
Code example - add a button called button1 to a form and add button1_Click to its click event, and then add this to the form:
bool continueWork = true;
private void button1_Click(object sender, EventArgs e)
{
Thread workThread = new Thread(performBackgroundWork);
workThread.IsBackground = true;
workThread.SetApartmentState(ApartmentState.STA);
workThread.Start();
for (long i = 0; i < long.MaxValue; i++)
{
if (!continueWork)
break;
button1.Text = i.ToString();
}
}
private void performBackgroundWork()
{
Thread.Sleep(1);
button1.BeginInvoke(new MethodInvoker(stopUIWork));
}
private void stopUIWork()
{
continueWork = false;
}
What is button1's text after it is clicked?
BeginInvoke adds the delegate to a queue (the message queue to be exact). So the answer is no, they won't get interrupted. Your button click handler is in fact executed due to a message in the message queue as well.
EDIT: Sorry, I forgot to answer your second question, but you can probably figure that out by yourself. The button text will be long.MaxValue - 1. You would probably want to execute stopUIWork without using BeginInvoke.

Categories

Resources