This program is writing numbers from 1 to 5000 in thread, but main form freezes anyway.
Where is an error? Thanks in advance.
Code:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Net;
using System.IO;
using System.Threading;
namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
int how, current;
bool job;
Object lockobj = new Object();
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
MessageBox.Show("Started!");
how = 5000;
current = 0;
job = true;
Thread worker = new Thread(Go);
worker.Name = "1";
worker.Start();
}
private void Go()
{
while (job)
{
if (current < how)
{
lock (lockobj)
{
current++;
}
log(string.Format("Thread #{0}: {1}", Thread.CurrentThread.Name, current));
}
else
{
job = false;
}
}
}
private void log(string text)
{
Action A = new Action(() =>
{
richTextBox1.AppendText(text + System.Environment.NewLine);
});
if (richTextBox1.InvokeRequired)
this.BeginInvoke(A);
else A();
}
}
}
Because most of your work will be spent in
if (richTextBox1.InvokeRequired)
this.BeginInvoke(A);
and while you invoke the form it is locked.
Do some real work, like Thread.Sleep(1000); :-) , instead of current++; and your form will be response between the updates.
It freezes because you are rendering on the textbox very quickly and the GUI doesn't have time to keep in sync. Remember that this rendering happens on the main GUI thread and by calling BeginInvoke to update the textbox so rapidly actually consumes all the resources of this main GUI thread. Try lowering the frequency at which you are logging to avoid this behavior.
Related
I'm trying to get a simple label value to change from another thread, and already tried 3 different threading mechanisms (Tasks, Thread, Background worker) and am now at a loss why the control won't update.
I have a method in an unrelated class like this:
public static void SetOverlayText(string text, bool fade = false)
{
Thread myThread = new Thread(FadeOverlayText);
OverlayForm.SetLabelText(text);
if (fade)
{
myThread.Start();
}
}
and
private static void FadeOverlayText()
{
OverlayForm.ClearLabelText();
}
My form is a regular windows form and has that method:
public void ClearLabelText()
{
this.Invoke((MethodInvoker)delegate
{
StatusText.Text = "Something should happen"
StatusText.Refresh();
});
}
The method appears to be getting called, but nothing happens.
You should not need Refresh.
This should work:
public void ClearLabelText()
{
if (StatusText.InvokeRequired)
{
this.Invoke((MethodInvoker)delegate
{
StatusText.Text = "Something should happen";
});
}
else
{
StatusText.Text = "Something should happen";
}
}
Are you shure, you use the correct control and at no other point the string is changed, so that it seems not to work? Please check every thing.
Also be sure, that you only call ClearLabelText once in your second thread, becuase after ClearLabelText is finished, the thread is not alive anymore.
This will update your text every second, as long as the application runs:
private static void FadeOverlayText()
{
var uiThread = <<Your UI Thread>>;
while(uiThread.IsAlive)
{
OverlayForm.ClearLabelText();
Thread.Sleep(1000);
}
}
EDIT:
here is a simple example i've made and it works. Additionaly to your StatusText label, I've added button1, which change the text too.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading;
using System.Windows.Forms;
namespace ThreadTest2
{
public partial class Form1 : Form
{
Thread mainThread = null;
public Form1()
{
InitializeComponent();
mainThread = Thread.CurrentThread;
Thread myThread = new Thread(FadeOverlayText);
myThread.Start();
}
private void FadeOverlayText()
{
while (mainThread.IsAlive)
{
ClearLabelText();
Thread.Sleep(1000);
}
}
public void ClearLabelText()
{
if (StatusText.InvokeRequired)
{
this.Invoke((MethodInvoker)delegate
{
StatusText.Text = "Something should happen";
});
}
else
{
StatusText.Text = "Something should happen";
}
}
private void button1_Click(object sender, EventArgs e)
{
StatusText.Text = "It works!";
}
}
}
One way to make this work is to use a timer that does
StatusText.Text= yourstring;
Every n milliseconds, and make your thread update the 'yourstring' variable to whatever you want.
Kindly help me. My idea is to continually print Numeric values form 0 to 1000 using thread concept. In case unexceptionally my application closes, how can I write the code WAITING for currently running thread tasks to complete.
Here by i mention sample code...
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Threading;
using System.Runtime.Remoting.Messaging;
using System.IO;
namespace Test_AsyncFactorCaller
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
public bool Work()
{
int nSleep = 100;
WriteMessage(string.Format("Going to Thread Sleep State for {0} sec", nSleep));
for (int i = 0; i < nSleep; i++)
{
WriteMessage(string.Format("Sleeping = {0}", i));
Thread.Sleep(1000);
}
WriteMessage("Going to Thread Wakeup State");
return true;
}
public void Work_Done(IAsyncResult result)
{
WriteMessage("Work_Done");
AsyncFactorCaller t = (AsyncFactorCaller)((AsyncResult)result).AsyncDelegate;
bool bResult = t.EndInvoke(result);
WriteMessage(string.Format("Result {0}",bResult));
result.AsyncWaitHandle.Close();
}
public void WriteMessage(string sMessage)
{
using (StreamWriter sw = new StreamWriter(#"C:\ThreadLog.txt", true))
{
sw.WriteLine(sMessage);
sw.Close();
}
}
private void btn_asyncCaller_Click(object sender, EventArgs e)
{
try
{
AsyncFactorCaller dGate_caller = new AsyncFactorCaller(Work);
AsyncCallback Completed_callBack = new AsyncCallback(Work_Done);
AsyncOperation asyncOperation = AsyncOperationManager.CreateOperation(null);
IAsyncResult result = dGate_caller.BeginInvoke(Completed_callBack, "Test thread");
}
catch (Exception ex)
{
MessageBox.Show(ex.Message.ToString());
}
}
public delegate bool AsyncFactorCaller();
}
}
If you are really sure that this is what you need to do, try using a WaitHandle.
AutoResetEvent _blocker = new AutoResetEvent(false);
//In background thread
_blocker.Set();
//Where you want to wait for it
_blocker.WaitOne();
I want to create a method which makes my application wait X number of seconds, then continues on down a line of scripts. For example, this is the code that I have so far, after reading many similar help topics:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
methods.WriteTextToScreen(label1, "Hello!");
methods.sleepFor(1);
methods.WriteTextToScreen(label1, "Welcome!");
methods.sleepFor(1);
methods.WriteTextToScreen(label1, "Allo!");
}
public class methods
{
public static int timeSlept;
public static void WriteTextToScreen(Label LabelName, string text)
{
LabelName.Text = text;
}
public static void sleepFor(int seconds)
{
timeSlept = 0;
System.Timers.Timer newTimer = new System.Timers.Timer();
newTimer.Interval = 1000;
newTimer.AutoReset = true;
newTimer.Elapsed += new System.Timers.ElapsedEventHandler(newTimer_Elapsed);
newTimer.Start();
while (timeSlept < seconds)
{
Application.DoEvents();
}
Application.DoEvents();
}
public static void newTimer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
timeSlept = IncreaseTimerValues(ref timeSlept);
Application.DoEvents();
}
public static int IncreaseTimerValues(ref int x)
{
int returnThis = x + 1;
return returnThis;
}
}
}
}
What I want to do is have my program do the methods.WriteTextToScreen(label1, "Hello!")
then wait for 1 second, then continue on in the same fashion. The problem is that the Form I'm displaying the text on doesn't show up at all until it has written "Allo!" onto the screen, so the first time it appears it already says that. Am I doing something wrong, or is there just no way to do this?
The form doesn't show until it has been constructed i.e. all the code in Form1 is run. See here for info on form constructors: http://msdn.microsoft.com/en-us/library/system.windows.forms.form.form.aspx
To fix your problem you could move the writeTextToScreen and sleep code into the forms on load method. See http://msdn.microsoft.com/en-us/library/system.windows.forms.form.onload.aspx
Is it possible to force the UI thread, to stop waiting for the task(s) to finish, update a UI control via Dispatcher, and then have the UI revert back to waiting for the task(s) to complete?
Ive just tried the following code, but its not working as it appears the
UpdatePB(int NewValue)
method is being executed by the non UI thread.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Threading.Tasks;
using System.Threading;
using System.Windows.Threading;
using System.Windows.Threading;
namespace UpdateControlViaDispatcherUITaskWaitAll
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
public void UpdatePB(int NewValue)
{
pb1.Value = NewValue;
}
private void btn1_Click(object sender, EventArgs e)
{
Task tk = Task.Factory.StartNew(() =>
{
Worker();
});
tk.Wait();
}
public void Worker()
{
int currentValue = 0;
for (int i = 0; i < 100; i++)
{
currentValue = i;
Dispatcher.CurrentDispatcher.Invoke(new Action(() =>
{
UpdatePB(currentValue);
}));
Thread.Sleep(1000);
}
}
}
}
Avoid blocking the UI thread:
private void Button_Click(object sender, RoutedEventArgs e)
{
Task.Factory
.StartNew(this.Worker)
.ContinueWith(this.OnWorkerCompleted);
}
public void Worker()
{
Dispatcher.Invoke(new Action(() =>
{
btn1.IsEnabled = false;
}));
// your stuff here...
}
private void OnWorkerCompleted(Task obj)
{
Dispatcher.Invoke(new Action(() =>
{
btn1.IsEnabled = true;
}));
}
Try to minimize calls to Dispatcher and also try using BackgroundWorker which supports automatic syncronization between background thread and UI thread with ProgressChanged and RunWorkerComplete events.
WPF Dispatcher has task queue of DispatcherOperation, so when you call tk.Wait(); it blocks Dispatcher thread until tk finishes. You haven't ability to pause this waiting and resume again, but only cancel DispatcherOperation. But in your case I assume you better disable button (or whole window) and enable it when tk finishes. So you should consider asynchronous waiting for tk to complete.
Delay.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
namespace LearnThread
{
class Delay
{
public int Convert()
{
int ErrorCode = 1;
//something
//takes long time. about 9 hours.
return ErrorCode;
}
}
}
Form1.cs
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Threading;
namespace LearnThread
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void btnStart_Click(object sender, EventArgs e)
{
Delay delay = new Delay();
Thread t = new Thread(delay.Convert);
//something
MessageBox.Show("Success");
}
}
}
Delay delay = new Delay(); is error here as it is expecting return value. I want the return value as it is contains errorcode. How can I do that? Background worker is better than Thread? Please help. (I should not lose control on the form when delay.Convert() is running.)
As mentioned by Juergen, you can make ErrorCode a class member and then access it once the thread has completed execution. This would require you to create a new instance of the Delay class if you are trying to run multiple Convert in parallel.
You can also use a delegate to get the return value to a variable in the btnStart_Click function as follows:
private void button1_Click(object sender, EventArgs e)
{
Delay delay = new Delay();
int delayResult = 0;
Thread t = new Thread(delegate() { delayResult = delay.Convert(); });
t.Start();
while (t.IsAlive)
{
System.Threading.Thread.Sleep(500);
}
MessageBox.Show(delayResult.ToString());
}
If you plan to run Convert in parallel here, you would have to create as many local variable as required or handle it someother way.
Make the ErrorCode a class member. This way you can get it afterwards.
class Delay
{
public int ErrorCode { get; private set; }
public void Convert()
{
ErrorCode = 1;
...
}
}
private void btnStart_Click(object sender, EventArgs e)
{
Delay delay = new Delay();
Thread t = new Thread(delay.Convert);
//something
int error = delay.ErrorCode;
MessageBox.Show("Success");
}