Ich have a little WinForms program, which has 1 Button and 1 Textbox. If i click the button then the programm counting from 1 to 100000 and shows in every step the current time in milliseconds in the textbox. The countingloop is running in a seperate thread.
public partial class Form1 : Form {
public delegate void myDelegate();
public myDelegate mydelegate;
public Form1() {
InitializeComponent();
mydelegate = new myDelegate(b);
}
private void button1_Click(object sender, EventArgs e) {
button2.Focus();
Thread t = new Thread(a);
t.Start();
}
private void Form1_KeyDown(object sender, KeyEventArgs e) {
Console.WriteLine(e.KeyCode);
}
public void a() {
for (int i = 0; i < 100000; i++) {
textBox1.BeginInvoke(mydelegate);
}
}
public void b() {
textBox1.Text = GetCurrentMilli().ToString();
textBox1.Refresh();
}
public static double GetCurrentMilli() {
DateTime Jan1970 = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
TimeSpan javaSpan = DateTime.UtcNow - Jan1970;
return javaSpan.TotalMilliseconds;
}
}
If i run this, the program works, but the gui is freezing till the loop is finished.
But why?
I have called BeginInvoke?!
If i replace
textBox1.BeginInvoke(mydelegate);
with
textBox1.Invoke(new MethodInvoker(b));
then it works without any freeze or problems.
But why?
When you call BeginInvoke you are scheduling a UI update to happen, and then continuing along with your program without waiting for that UI update to happen. When you do this just a few times you're fine, but the problem that you're having is that you're sending in 100,000 requests all at once, and it's going to take the UI some time to get through all of those requests, and nothing else is going to be able to be done in that time because any new UI updates go to the end of the line, and won't be performed until the other requests are finished.
While there are ways to keep your general approach the same and try to let other operations cut to the front of the line, the proper approach is to avoid the problem in the first place. You have no need to be sending 100,000 updates to single textbox at once.
If you want the textbox to have the appearance of a clock, in which it ticks up, then a Timer would be a good tool for the job; you can handle the Tick event to update the textbox every second, quarter second, or some other more "human time" interval.
If the idea is to update the UI with the progress of some long running operation, then you simply want to ensure that you don't update progress quite so often. Update progress every few dozen iterations of your loop, instead of every single one, for example.
You may need to call UpdateLayout in between, not sure if it needs to be invoked to prevent cross-thread exception
public void a() {
for (int i = 0; i < 100000; i++) {
textBox1.BeginInvoke(mydelegate);
textBox1.UpdateLayout();
}
}
Related
So I've got an application that employs a filesystemWatcher and triggers an event just fine. The FSW will trigger a bunch of times pretty close together. I want to create a function that triggers say an hour after the last time the FSW was triggered.
I first tried using a backgroundworker: (All code is shortened for clarity)
namespace Devo
{
public partial class Form1 : Form
{
BackgroundWorker bw = new BackgroundWorker();
private void fileSystemWatcher_Created(object sender, FileSystemEventArgs e)
{
if (bw.IsBusy)
{
bw.CancelAsync(); //this is to, in a way, reset the timer for the delayed method.
}
//do a lot of stuff
bw.RunWorkerAsync();
}
private void backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
Stopwatch sw = new Stopwatch();
sw.Start();
while(sw.ElapsedMilliseconds < 180000)
{
if (bw.CancellationPending == true)
{
sw.Stop();
sw.Reset();
e.Cancel = true;
return;
}
}
sw.Stop();
sw.Reset();
DelayedMethod();
}
}
}
This didn't work as the second time bw.RunWorkerAsync() was called it was apparently busy, even though the call to bw.CancelAsync().
My next attempt involved a regular thread as I read somewhere on SO (can't find the link now) that one could not "restart" a backgroundWorker as I am trying to do.
The thread attemp is nearly identical but I thought I'd try in since there might be some constraints within the backgroundWorker that is not present in a regular thread. I thought.
namespace Devo
{
public partial class Form1 : Form
{
Thread PWC_counter_thread = new Thread(PWC_Counter);
private void fileSystemWatcher_Created(object sender, FileSystemEventArgs e)
{
if (PWC_counter_thread.IsAlive)
PWC_counter_thread.Abort();
//do a lot of stuff
PWC_counter_thread.Start();
}
static void PWC_Counter()
{
Thread.Sleep(180000);
DelayedMethod();
}
}
}
But this gave me the same error. On the second call to PWC_counter_thread.Start() is was busy.
I'm assuming that a race condition is not present as the second thread waits for, in this example, 3 minutes, and the initial FSW method takes a good full second to execute, therefore I believe that the call to .Abort() and .CancelAsync() both are done before their respective methods are completed.
Now for the questions:
Is it possible to restart a thread in the fashion I am trying? If so, what am I doing wrong?
Should I delay my method call in another way? If so, tips?
EDIT/UPDATE/SOLUTION
I never got starting and stopping a thread to work as I wanted so I found another solution to my situation.
The situation was that I had a second thread that worked as a sort of timer where it would call a method after a set amount of time. My first thread did some work and upon finishing it would start the second thread. If the first thread got fired up again before the timer-thread had finished it was supposed to kill the thread and restart it.
This proved, for me, to be difficult to get the way I wanted. So I instead took another approach towards my wanted end result. Instead of restarting the thread I simply restarted the stopwatch that my second thread was using as a counter. This gave me the result I wanted. It's probably bad practice but it works.
In your BackgroundWorker example you probably have an issue with racing. CancelAsync(), as its name implies, is an asynchronious call, meaning that BackgroundWorker does not stop working immediately and it might still work when try to restart it. To avoid that, you should subscribe to RunWorkerCompleted event and wait for it to fire before calling bw.RunWorkerAsync(); again. For example:
public Form1()
{
bw = new BackgroundWorker();
bw.RunWorkerCompleted += OnCompleted;
}
private BackgroundWorker bw;
private ManualResetEvent completed = new ManualResetEvent(false);
private void OnCompleted(object sender, RunWorkerCompletedEventArgs e)
{
completed.Set();
}
private void fileSystemWatcher_Created(object sender, FileSystemEventArgs e)
{
if (bw.IsBusy)
{
bw.CancelAsync();
completed.WaitOne();
}
//do a lot of stuff
completed.Reset();
bw.RunWorkerAsync();
}
You have multiple issues with your Thread-based example.
You should never call Thread.Abort(). Instead, you should implement a cancellation mechanism, similar to that of BackgroundWorker. Make a bool field (_isCancelled or something) and check it periodically in thread delegate.
You can not reuse a Thread object. You should always create a new one.
You would be best off encapsulating this in a class, and use a System.Threading.Timer to detect the inactivity.
Here's an example I put together. The idea is that you create an InactivityDetector with the appropriate inactivity threshold (an hour in your case) and a callback method that will be called when that period of inactivity is exceeded.
You have to call InactivityDetector.RegisterActivity() whenever activity is detected (e.g. in your case a file creation is detected).
Once the inactivity callback has been issued, it will not be called again until RegisterActivity() has been called again (this prevents multiple callbacks for the same period of extended inactivity).
Your code would pass DelayedMethod for the inactivity Action delegate.
Note that the callback is on a separate thread!
(Also note that I didn't put in any parameter validation, to keep the code shorter.)
using System;
using System.Threading;
namespace ConsoleApp1
{
sealed class Program
{
void test()
{
using (var inactivityDetector = new InactivityDetector(TimeSpan.FromSeconds(2), inactivityDetected))
{
for (int loop = 0; loop < 3; ++loop)
{
Console.WriteLine("Keeping busy once a second for 5 seconds.");
for (int i = 0; i < 5; ++i)
{
Thread.Sleep(1000);
Console.WriteLine("Registering activity");
inactivityDetector.RegisterActivity();
}
Console.WriteLine("Entering 3 second inactivity");
Thread.Sleep(3000);
inactivityDetector.RegisterActivity();
}
}
}
static void inactivityDetected()
{
Console.WriteLine("Inactivity detected.");
}
static void Main(string[] args)
{
new Program().test();
}
}
public sealed class InactivityDetector: IDisposable
{
public InactivityDetector(TimeSpan inactivityThreshold, Action onInactivity)
{
_inactivityThreshold = inactivityThreshold;
_onInactivity = onInactivity;
_timer = new Timer(timerCallback, null, (int)inactivityThreshold.TotalMilliseconds, -1);
}
public void RegisterActivity()
{
_timer.Change(-1, -1);
_timer.Change((int)_inactivityThreshold.TotalMilliseconds, -1);
}
private void timerCallback(object state)
{
_timer.Change(-1, -1);
_onInactivity();
}
public void Dispose()
{
_timer.Dispose();
}
private readonly TimeSpan _inactivityThreshold;
private readonly Action _onInactivity;
private readonly Timer _timer;
}
}
I have a problem about using multithreading in c#. I have a complex computing thing that must run on the main thread means it must run on the main window.
My program runs like this :
First it will open a new window and user can input parameter
Then it will run a complex computing that must run in the main thread ( i can't change this, i'm using library that prevent call the computing thing in different thread, if i'm using different thread it will come with an exception).
When the simulation run, it will also open a new window that monitoring the computing, it will also plot the graphic for it, and i need it to be real time.
Problem: Since the long time complex computing is running on the main thread, the monitoring window will be hang and not responding. Is there any tricky way to solve this? Can i use different thread to run the monitoring window? the main form is okay for not responding, but not okay for monitoring window. Here i put some code that resembles my program, not my real program, but the flow is the same.
Main Form(main thread that will run the computing thing,and input parameter from user)
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
compute c = new compute();
c.computing();
}
}
Graph Form (Monitoring Graph that will plot chart, needed to be real time)
public graphic()
{
InitializeComponent();
}
private void label1_Click(object sender, EventArgs e)
{
}
public void updateLabel(int count)
{
label1.Text = "Current i value: " + count;
}
Compute class (doing the computing thing)
class compute
{
delegate void writeToForm(int i);
graphic g = new graphic();
public compute()
{
g.Show();
}
public void computing()
{
//THIS PART MUST RUN ON THE MAIN THREAD, DON'T MAKE IT ON THE OTHER THREAD
//IT IS A MUST
int count = 0;
//THIS IS THE LONG TIME COMPUTING THING
for (int i = 0; i < 100; i++)
{
count += i;
updateLabel(count);
Thread.Sleep(2000);
}
}
private void updateLabel(int count)
{
if (g.InvokeRequired)
{
g.Invoke(new writeToForm(updateLabel), new object[] { count });
return;
}
g.updateLabel(count);
}
}
I can't do anything about the method must run in the main thread, i'm using library that prevent its method to be ran from other method, thanks..
If it is must for you to perform computation in the main thread, modify your code as below
public void computing()
{
//THIS PART MUST RUN ON THE MAIN THREAD, DON'T MAKE IT ON THE OTHER THREAD
//IT IS A MUST
int count = 0;
//THIS IS THE LONG TIME COMPUTING THING
for (int i = 0; i < 100; i++)
{
count += i;
updateLabel(count);
Thread.Sleep(2000);
Application.DoEvents(); //tell windows to process pending message.
}
}
A possible workaround would be to write your monitoring form in another program, which can be launched by your main program before the computing method is executed. Yes, your main form will still hang (but you say this isn't a problem?).
You could communicate with your main application, possibly by opening a local TCP connection (which can run on a different thread without a problem). This could send commands to the monitoring application (or any other connected TCP clients if you wish), which would receive these and use the Control.Invoke method to update the UI.
You could try and call Application.DoEvents after updating the graph in the second window. This processes all window messages, which should allow your second window to be updated.
I have a Grid control and clicking on each row does some background job to load the data. Each background job is performed on a thread pool thread. When user clicks on the items quickly, lot of requests to load data will be queued. I want to minimize this by providing a delay after clicking each row. There will be some delay before firing the request to load the data.
I am thinking about using DispatcherTimer class. Something like,
readonly DispatcherTimer dt = new DispatcherTimer();
private void Clicked(object sender, RoutedEventArgs e)
{
dt.Interval = TimeSpan.FromSeconds(2);
dt.Stop();
dt.Start();
}
private void DtOnTick(object sender, EventArgs args)
{
// Fire a thread and do data loading
}
Is this the correct way to approach the problem?
Any suggestions would be appreciated!
How about disabling the control until the job is finished? Or disabling once the queue of jobs to do reaches a certain size? This would be a simple solution to prevent users from "clicking too much". And this way the delay would scale with the efficiency of your solution/speed of the computer.
The way you're trying to do it would just delay the problem itself for 2 seconds. All the clicks would just be handled two seconds later.
You might try to use a worker thread. Lets say you use a queue which takes information about each item that was clicked at the time it was clicked. An existing thread, created when the class is created, is notified when new items are added to the queue. The thread takes the first item, processes it, updates the UI. If there are more items, it takes the next one, processes it, etc. When there are no more items, the thread goes to sleep until new items are available (ManualResetEvent will help here).
The pattern would be:
void ItemClicked(...)
{
lock (WorkQueue)
{
QueueNewClickItem(...);
m_workToDo.Set();
}
}
void WorkerThread(...)
{
bool threadShouldEnd = false;
while (!threadShouldEnd)
{
if (WaitHandle.WaitAny(m_workToDo, m_endThread) == 0)
{
lock (WorkQueue)
{
CopyAllPendingWorkItemsToListInThread();
ClearWorkQueue();
m_workToDo.Reset();
}
while (!AllLocalItemsProcessed)
{
ProcessNextWorkItem();
}
}
else
{
threadShouldEnd = true;
}
}
}
What you actually want to do is something like this:
private DateTime? _NextAllowedClick;
private void Clicked(object sender, RoutedEventArgs e)
{
if (_NextAllowedClick != null && DateTime.Now < _NextAllowedClick)
{
return;
}
_NextAllowedClick = DateTime.Now + new TimeSpan(0, 0, 0, 2);
...
}
I am trying to generate a random fruit and display it on GUI in a label. I am using this code to do it.
partial class Form1 : Form
{
int MagicNumber = 0;
List<string> NameList = new List<string>();
Random r = new Random();
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
NameList.Add("Apples");
NameList.Add("Pears");
NameList.Add("Oranges");
NameList.Add("Bananas");
NameList.Add("Kiwi");
for (int i = 0; i < 8; i++)
{
Thread t = new Thread(new ThreadStart(Display));
t.Start();
label1.Text = NameList[MagicNumber];
Thread.Sleep(1000);
}
}
private void Display()
{
MagicNumber = r.Next(5);
}
}
The problem is the fact that in GUI i see only the last result of fruits choice and not how they are skipped from an iteration to other. I thought that this code will give me the possibility to see how fruits changes until the last was chosen , when i is 8.
Please if you have an idea why this code is not displaying how the fruits are chosen in label give me a hand !
Thanks.
You seem to be confusing timers and threads. In this case, I think what you want is a timer; specifically, System.Windows.Forms.Timer. You might do something like this:
partial class Form1 : Form
{
Timer timer = new Timer();
private void button1_Click(object sender, EventArgs e)
{
int i = 0;
timer.Tick += (s, e) =>
{
if (i < 8)
{
label1.Text = nameList[r.Next(5)];
i++;
}
else
timer.Stop();
};
timer.Interval = 1000;
timer.Start();
}
}
The idea is that you set a timer to tick once a second, and then each time it ticks, you change the label and increment the counter until it reaches 8 -- at which point it stops. You always want to make sure you call Start() after you've set Tick and Interval; otherwise, under some strange circumstances, the timer might tick before you have a chance to change the settings.
Alternatively, you could use threading and Sleep(), in which case it might look like this:
private void button1_Click(object sender, EventArgs e)
{
Thread t = new Thread(new ThreadStart(Display));
t.Start();
}
private void Display()
{
for(int i = 0; i < 8; i++)
{
label1.Text = NameList[r.Next(5)];
Thread.Sleep(1000);
}
}
Thread.Sleep() always sleeps the thread that it's called from -- so maybe this is what you meant to do.
However, this might throw a thread synchronization exception -- Forms prevents you from accessing UI controls from another thread, since it might be in an invalid state (i.e. in the middle of rendering or doing something else that's volatile). System.Windows.Forms.Timer actually runs on the UI thread, so it's easier to manage.
Your approach is flawed, but you may want to understand what is going on in your code, as it may help you find a better approach:
for (int i = 0; i < 8; i++)
{
Thread t = new Thread(new ThreadStart(Display));
t.Start();
label1.Text = NameList[MagicNumber];
Thread.Sleep(1000);
}
You are looking through, creating eight threads every time the button is clicked. Do you have a reason to create eight threads? If so, you may want to create them once, inside your init function and reuse them.
Then there is a race here in that your threads may not have had time to change MagicNumber before it is used, as the loop starts the threads then immediately changes the text, before going to sleep.
The sleep is another problem, as you haven't gotten off of the main (event) thread, so the text isn't changed until you exit that event handler.
If you want to see the text changing, then you will need to get off of the main thread, and in a second thread go through and do the loop of eight.
Then, you can put that thread to sleep, and since the main thread was free to make the change you will see it.
Here is an article from MS that is a bit dated, but the basic idea should help you:
http://msdn.microsoft.com/en-us/magazine/cc188732.aspx
Now you can use lambda expressions for your threads, as shown here:
http://www.rvenables.com/2009/01/threading-tips-and-tricks/
Just call Application.DoEvents(); after assigning text to label - that will refresh UI.
BTW I don't understand why you are using threads to generate random numbers
The problem is that when you execute an event handler or a function called from it, the changes are rendered at the end. Try changhing the label text inside the thread where you get the random number. You also have to set the CheckForIllegalCrossThreadCalls property to false in the form constructor.
Your observed problem of the form not refreshing is due to your function blocking the GUI thread and preventing a redraw of the window while its running. And it's continuously running for 8 seconds. The GUI thread needs to handle messages to allow a window to be redrawn.
But apart from what you observed it has has at least two theoretical problems related to threading:
The read of MagicNumber isn't volatile, so the compiler may read it only once and cache the result. It probably won't do that in practice since the code between each reading of the variable is so complicated that it can't guarantee that they won't affect the variable.
r.Next isn't threadsafe. So calling it from two different threads at the same time can corrupt the Random instance. Won't happen in practice either since the delay is so long that one thread will most likely have finished before the next one starts.
There is a much better way to choose a random item:
label1.Text = NameList.OrderBy(f => Guid.NewGuid()).First();
Randomizing on different threads is a bad idea in of itself.
I'm working on a card game in C# for a project on my Intro to OOP paper and have got the game working now but am adding "flair" to the GUI.
Currently cards are dealt and appear on the UI instantaneously. I want to have to program pause for a moment after dealing a card before it deals the next.
When a game is started the following code runs to populate the PictureBoxes that represent them (will be a loop eventually):
cardImage1.Image = playDeck.deal().show();
cardImage2.Image = playDeck.deal().show();
cardImage3.Image = playDeck.deal().show();
cardImage4.Image = playDeck.deal().show();
cardImage5.Image = playDeck.deal().show();
...
I have tries using System.Threading.Thread.Sleep(100); between each deal().show() and also inside each of those methods but all it achieves is locking up my GUI until all of the sleeps have processed then display all of the cards at once.
I have also tried using a combination of a timer and while loop but it resulted in the same effect.
What would be the best way of achieving the desired result?
The problem is that any code that you run on the UI will block the UI and freeze the program. When your code is running (even if it's running Thread.Sleep), messages (such as Paint or Click) sent to the UI will not be processed (until control returns to the message loop when you exit your event handler), causing it to freeze.
The best way to do this is to run on a background thread, and then Invoke to the UI thread between sleeps, like this:
//From the UI thread,
ThreadPool.QueueUserWorkItem(delegate {
//This code runs on a backround thread.
//It will not block the UI.
//However, you can't manipulate the UI from here.
//Instead, call Invoke.
Invoke(new Action(delegate { cardImage1.Image = playDeck.deal().show(); }));
Thread.Sleep(100);
Invoke(new Action(delegate { cardImage2.Image = playDeck.deal().show(); }));
Thread.Sleep(100);
Invoke(new Action(delegate { cardImage3.Image = playDeck.deal().show(); }));
Thread.Sleep(100);
//etc...
});
//The UI thread will continue while the delegate runs in the background.
Alternatively, you could make a timer and show each image in the next timer tick. If you use a timer, all you should do at the beginning is start the timer; don't wait for it or you'll introduce the same problem.
Normally I'd simply recommend a function like this to perform a pause while allowing the UI to be interactive.
private void InteractivePause(TimeSpan length)
{
DateTime start = DateTime.Now;
TimeSpan restTime = new TimeSpan(200000); // 20 milliseconds
while(true)
{
System.Windows.Forms.Application.DoEvents();
TimeSpan remainingTime = start.Add(length).Subtract(DateTime.Now);
if (remainingTime > restTime)
{
System.Diagnostics.Debug.WriteLine(string.Format("1: {0}", remainingTime));
// Wait an insignificant amount of time so that the
// CPU usage doesn't hit the roof while we wait.
System.Threading.Thread.Sleep(restTime);
}
else
{
System.Diagnostics.Debug.WriteLine(string.Format("2: {0}", remainingTime));
if (remainingTime.Ticks > 0)
System.Threading.Thread.Sleep(remainingTime);
break;
}
}
}
But there seems to be some complication in using such a solution when it is called from within an event handler such as a button click. I think the system wants the button click event handler to return before it will continue processing other events because if I try to click again while the event handler is still running, the button depresses again even though I'm trying to drag the form and not click on the button.
So here's my alternative. Add a timer to the form and create a dealer class to handle dealing with cards by interacting with that timer. Set the Interval property of the timer to match the interval at which you want cards to be dealt. Here's my sample code.
public partial class Form1 : Form
{
CardDealer dealer;
public Form1()
{
InitializeComponent();
dealer = new CardDealer(timer1);
}
private void button1_Click(object sender, EventArgs e)
{
dealer.QueueCard(img1, cardImage1);
dealer.QueueCard(img2, cardImage2);
dealer.QueueCard(img3, cardImage1);
}
}
class CardDealer
{
// A queue of pairs in which the first value represents
// the slot where the card will go, and the second is
// a reference to the image that will appear there.
Queue<KeyValuePair<Label, Image>> cardsToDeal;
System.Windows.Forms.Timer dealTimer;
public CardDealer(System.Windows.Forms.Timer dealTimer)
{
cardsToDeal = new Queue<KeyValuePair<Label, Image>>();
dealTimer.Tick += new EventHandler(dealTimer_Tick);
this.dealTimer = dealTimer;
}
void dealTimer_Tick(object sender, EventArgs e)
{
KeyValuePair<Label, Image> cardInfo = cardInfo = cardsToDeal.Dequeue();
cardInfo.Key.Image = cardInfo.Value;
if (cardsToDeal.Count <= 0)
dealTimer.Enabled = false;
}
public void QueueCard(Label slot, Image card)
{
cardsToDeal.Enqueue(new KeyValuePair<Label, Image>(slot, card));
dealTimer.Enabled = true;
}
}
The cheap way out would be to loop with calls to Application.DoEvents() but a better alternative would be to set a System.Windows.Forms.Timer which you would stop after the first time it elapses. In either case you'll need some indicator to tell your UI event handlers to ignore input. You could even just use the timer.Enabled property for this purpose if it's simple enough.
I would try puting the code that deals the deck ( and calls Thread.Sleep) in another thread.