Change page controls value after thread finished? [duplicate] - c#

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
asp.net update UI using multi-thread
I started a thread to process on background.And i want to change my label on page after finished proceess.
But my value doesn't change.
my sample :
protected void Page_Load(object sender, EventArgs e)
{
new Thread(() => ActionStart()) { IsBackground = true }.Start();
}
bool _shouldStop = false;
public void ActionStart()
{
while (!_shouldStop)
{
// ....
requestStop();
}
//thread finished
ChangeValue();
}
private void ChangeValue()
{
lab.Text = "Changed";
}
private void requestStop()
{
_shouldStop = true;
}

I'm afraid running this code on a Thread won't help you because you can't make sure the thread will complete before it's to late considering the page life cycle. Generally, the last chance you got to alter a control's value is the it's PreRender event, but that depends on how it has been developed.
However, if you need to do processing in parallel, you still can use a Parallel.Foreach, for instance, inside a page event like PageLoad to take advantage of parallelism without being asynchronous (Parallel.Foreach will only return when all threads complete).

Related

Why thread IsAlive returns false even after executing the last command inside the thread [duplicate]

This question already has answers here:
How can I wait for a thread to finish with .NET?
(11 answers)
Closed 3 years ago.
I started a thread inside a label click event. The thread did its job. I even got the last message box inside the thread. I want to execute some more instructions inside the label click event after finishing the thread. So I used an if statement with IsAlive property of the thread. Seems like thread's IsAlive property value is always true. How? How to make sure that all thread gonna end when I close the application or is it gonna end naturally when I close the application?
private void lbl_Calc_Click(object sender, EventArgs e)
{
Label label = (Label)sender;
//new thread to do the calc
Thread t = new Thread(() => ThreadedMethodForCalc(label));
t.Start();
//checking the IsAlive property
//but doesn't work
if (t.IsAlive==false)
{
MessageBox.Show("please enter the data");
//more instruction here
}
}
private void ThreadedMethodForCalc(Label label)
{
//calculation. not shown
MessageBox.Show("end of thread"); //executed
return;
}
If you must use Thread, you can use Thread.Join to wait until the thread has completed.
private void lbl_Calc_Click(object sender, EventArgs e)
{
Label label = (Label)sender;
//new thread to do the calc
Thread t = new Thread(() => ThreadedMethodForCalc(label));
t.Start();
t.Join(); //wait until the thread completes
MessageBox.Show("please enter the data");
//more instruction here
}
However, that will also lock up your UI while the thread is running, which means there isn't much difference between this and just calling ThreadedMethodForCalc directly.
To avoid that, you can use async/await and Task.Run:
private async void lbl_Calc_Click(object sender, EventArgs e)
{
Label label = (Label)sender;
//new thread to do the calc
await Task.Run(() => ThreadedMethodForCalc(label));
MessageBox.Show("please enter the data");
//more instruction here
}
That will leave your UI responsive to user input while ThreadedMethodForCalc runs. However, you may have to disable some controls on your form to make sure that the user cannot do things they shouldn't be doing while that operation is running, and enable them again after. But that is a decision you'll have to make.
There is more information about asynchronous programming here: Asynchronous programming with async and await

Terminate new thread with delegate when winform is closed in c# [duplicate]

This question already has answers here:
How to stop BackgroundWorker on Form's Closing event?
(12 answers)
Closed 6 years ago.
I tried to make a new thread using the winform application. Here is my sample code.
public static bool stop = false;
private Thread mythread(){
Thread t = new Thread(delegate() {
while(!stop){
// Something I want to process
}
});
return t;
}
private Button1_click(object sender, EventArgs e){
stop = true; // I know it doesn't work
this.Dispose();
this.Close();
}
public Main(){
InitializeComponent();
Thread thread = mythread();
thread.Start();
}
When the button1 is clicked, new thread and winform should be terminated, but new thread is still working. Is there any way to terminate the new thread?
ps: I tried to change my code reffering to the MSDN site example but it only made it more complicated.
This is a problem of visibility of variables in other threads... try this:
private static int stop = 0;
private Thread mythread(){
Thread t = new Thread(delegate() {
while(Thread.VolatileRead(ref stop) == 0){
// Something I want to process
}
});
return t;
}
private Button1_click(object sender, EventArgs e){
Thread.VolatileWrite(ref stop, 1);
this.Dispose();
this.Close();
}
public Main(){
InitializeComponent();
Thread thread = mythread();
thread.Start();
}
Notes:
Aborting threads is not recommended.
Making stop volatile should be enough in your example.
The presented code is using Thread.VolatileRead and Thread.VolatileWrite to update the variable. Using int instead of bool, and not using the volatile keyword will ease transition to Interlocked if you need it.
I recommend the series of articles Threading in C# by Joseph Albahari

Is it possible to change views, once during the start of an event handler and once during end?

I get data from database on a click.
I have an event handler which when triggered should show "data retrieving..." in status bar and should change to "Ready" again just before the event handler ends.
But the text updates only once, the second Ready one. How is it generally done?
private void Next_Click(object sender, RoutedEventArgs e){
this.footerText = "Waiting for dataRetreival";
someRandomTimeTakingMethod(); //Gets Data from DB.
this.footerText = "Ready";
}
Even though code executes line 2, the view updates only when the function is over, ie only the second one actually works.
You should put your data-intensive work on a background thread so the UI can update properly. This provides the best user experience.
To elaborate on FZysset's answer with some code...
private async void Next_Click(object sender, RoutedEventArgs e)
{
footerText.Text = "Waiting for dataRetreival";
IsEnabled = false;
await SomeRandomTimeTakingMethodAsync();
IsEnabled = true;
footerText.Text = "Ready";
}
private async Task SomeRandomTimeTakingMethodAsync()
{
await Task.Delay(TimeSpan.FromSeconds(new Random().Next(2, 5)));
// or await Task.Run(() => { ... });
}
The above example allows you to leverage await/async that was introduced in .NET 4.5. Notice how nicely it flows? No nonsense!
We're putting stuff onto the background thread so the UI can remain unblocked (thus it will show your updates to your status bar and allow user interaction.) Of course, you have to be careful not to update anything on the UI from your background thread.
If you are using an older version of .NET, you can just use TPL without async/await:
private void Next_Click(object sender, RoutedEventArgs e)
{
footerText.Text = "Waiting for dataRetreival";
IsEnabled = false;
Task.Factory.StartNew(() =>
{
SomeRandomTimeTakingMethod();
}).ContinueWith(t =>
{
IsEnabled = true;
footerText.Text = "Ready";
}, TaskScheduler.FromCurrentSynchronizationContext());
}
private void SomeRandomTimeTakingMethod()
{
Thread.Sleep(TimeSpan.FromSeconds(new Random().Next(2, 5)));
}
Two important things to note about the latter example:
You must provide TaskScheduler.FromCurrentSynchronizationContext() to the ContinueWith call, or you will encounter exceptions because the continuation is not on the UI thread. You must get the context in a method that isn't running on a background thread.
You will want to check for exceptions on the Task object in your ContinueWith.
This example is very rudimentary though. If you were to have a bunch of background operations kicked off with click handlers, you'd want to give yourself some helper classes/services to make life easier. (And investigate MVVM, which I cannot tell if you are using.)
A colleague of mine gave a presentation on using various asynchronous patterns in C# and .NET. You can check it out here: https://github.com/mtusk/TplLunchAndLearn
That's because you're "someRandomTimeTakingMethod" is launched on the UI Thread. Therefore it will not update the view until it is finished.
To go around this you have the following possibilities :
Make your method "someRandom..." asynchronous with a task, and use the await operator : http://msdn.microsoft.com/en-us/library/hh191443.aspx
Launch your randomTimeTaking method into a thread, and launch an event when your execution is finished, to update the footer text
I strongly recommend you the first option, for some sample : http://msdn.microsoft.com/en-us/library/hh873191.aspx
You need to run those lines asynchronously. You can do that using the Task class:
private void Next_Click(object sender, RoutedEventArgs e){
Task.Factory.StartNew(() => footerText = "Waiting for dataRetreival");
someRandomTimeTakingMethod(); //Gets Data from DB.
Task.Factory.StartNew(() => footerText = "Ready");
}
There is one way to do it using Dispatcher. The original post is here.
The code is:-
private void Next_Click(object sender, RoutedEventArgs e){
UpdateUI("Please wait for data retrieval", delegate() { someRandomTimeTakingMethod(); });
this.footerText = "Ready";
}
public delegate void NoArgsDelegate();
public void UpdateUI(string description, NoArgsDelegate operation)
{
this.FooterText= description;
DispatcherFrame frame = new DispatcherFrame();
DispatcherOperation dispatcherOperation = Dispatcher.CurrentDispatcher.BeginInvoke(DispatcherPriority.ContextIdle, operation);
dispatcherOperation.Completed += delegate(object sender, EventArgs e)
{
frame.Continue = false;
};
Dispatcher.PushFrame(frame);
}
If my understanding is right, this uses Asynchronous programming, not different thread. The thread will update UI first and then call the someRandomTimeTakingMethod().

Automatically call a task every 5 seconds

i have an Infragistics carousel control which i want to call "next" on every 5 seconds. However im not sure how to do this without a while true.
Any help would be great, thanks!
Currently its kicked off from a click, starting a task within a while true. Obviously this i what i want to avoid.
private void GoToNext(object sender, MouseButtonEventArgs e)
{
while (true)
{
Task task = new Task(() => MyCarousel.Dispatcher.BeginInvoke(new next(goToNext), null));
task.Start();
}
}
private bool goToNext()
{
Thread.Sleep(15);
MyCarousel.ExecuteCommand(Infragistics.Windows.Controls.XamCarouselPanelCommands.NavigateToNextItem);
return true;
}
Timers are used for this in general, sitting in the background and triggering every so often. There are a number in .NET (in System.Timers and elsewhere) and which one is best depends on your particular scenario.

Getting Cross-thread operation not valid [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Cross-thread operation not valid: Control accessed from a thread other than the thread it was created on
public void CheckUnusedTabs(string strTabToRemove)
{
TabPage tp = TaskBarRef.tabControl1.TabPages[strTabToRemove];
tp.Controls.Remove(this);
TaskBarRef.tabControl1.TabPages.Remove(tp);
}
I am trying to close a tab in the tabcontrol of windows application using the above code and i encountered the error:
Cross-thread operation not valid.
How to solve this ?
You can only make changes to WinForm controls from the master thread. You need to check whether InvokeRequired is true on the control and then Invoke the method as needed.
You can do something like this to make it work:
public void CheckUnusedTabs(string strTabToRemove)
{
if (TaskBarRef.tabControl1.InvokeRequired)
{
TaskBarRef.tabControl1.Invoke(new Action<string>(CheckUnusedTabs), strTabToRemove);
return;
}
TabPage tp = TaskBarRef.tabControl1.TabPages[strTabToRemove];
tp.Controls.Remove(this);
TaskBarRef.tabControl1.TabPages.Remove(tp);
}
call using invoke, because you're accessing the GUI thread using another thread
this.Invoke((MethodInvoker)delegate() {CheckUnusedTabs(""); });
When using threads and UI controls, in winforms, you need to use InvokeRequired to make changes to the controls.
EDIT.
added an example.
Form, with button and label.
try
private void button2_Click(object sender, EventArgs e)
{
Thread thread = new Thread(UpdateProcess);
thread.Start();
}
private void SetLabelText(string val)
{
label1.Text = val;
}
delegate void m_SetLabel(string val);
private void UpdateProcess()
{
int i = 0;
while (true)
{
if (label1.InvokeRequired)
{
m_SetLabel setLabel = SetLabelText;
Invoke(setLabel, i.ToString());
}
else
label1.Text = i.ToString();
i++;
Thread.Sleep(500);
}
}
Cross thread not valid exception is due to the UI controls being accessed from other threads than main thread.see this
http://helpprogramming.blogspot.com/2011/10/invalid-cross-thread-operation.html
Set the following variable:
CheckIllegalCrossThreadValidation = false

Categories