Thread issue in C# - c#

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.

Related

Stop Threads from spawning

Having the Code Below in Windows forms.
private bool test = false;
private async void button1_Click(object sender, EventArgs e)
{
await Task.Run(() =>
{
test = !test ;
textBox2.Text = test.ToString(); // Each time i click text box switches to true and false
for (int i = 0; i < 1000000; i++)
{
textBox1.Text = i.ToString();
}
});
textBox2.Text = "Done"; // This will never happen until the task is done
}
If i Click button the textbox text Changes from 0 to 1000000.
Since i use async/await. the form will not freeze and I can see the Textbox Counting from 0 to 1000000.
But the problem is if i click the button again another thread spawn and textbox value changes by two threads. and you can see two counters from two threads changing textbox value.
if you click again you get 3 threads, 4 threads etc....Also textbox2 changes to true, false, true ....
This was just a test for me to see how actually async await works.
But i think im using it wrong. im afraid if i use async in my projects and end up to this situation.
How can i stop Threads spawning from single async method.
Currently I think each time i press the button New async Method spawns.
Here is What i see.
There's some confusion in the comments about what's happening. Especially since the posted code shouldn't actually work (it's not thread-safe).
The simplest way to reproduce this (drop a Button and a Label on a Form):
private async void button1_Click(object sender, EventArgs e) // add the async
{
for (int i = 0; i < 1000; i++)
{
label1.Text = "" + i;
await Task.Delay(100);
}
}
You can make this run multiple loops at once, you can increase the Delay to see that better. Note that there are no extra Threads involved, it all runs on the main GUI thread.
The keys here are async and await, they make the compiler turn a call to this method into a state machine and that way it can interleave the execution of multiple loops at once. Think of it as a method that can be paused at the await call and be resumed later. All on the same thread.
More importantly, it interleaves the loop(s) with the main thread so that that can continue to handle input messages and update the screen.
That was the 'why' part.
The how to solve it part depends on what you actually want, the now deleted answer from #CSharpie shows the basic pattern: use a boolean field as a guard, or disable & enable the Button.
Disable the button after pressed and and enable it when it finishes.

C# Random number generator stuck in loop

I just started programming after taking a long break and am running into a little issue.
I am using VS2013 Desktop and am creating this as a GUI program.
My problem is that I created a working random number generator when the method used to call the logic method runs once. The number(s) gets generated, text updated and all is good. When it goes into a loop, it doesn't update the text property of the object I'm modifying until it finishes the entire loop or gets broken. The program basically hangs when I run it when the loop gets executed and I have to force it to close.
At the moment I would like to set the generator to run infinitely in the background until I press another button to stop it.
I am new to programming and this probably has all sorts of issues with it so I would be grateful for any feedback on structure and other practices if anything is out of order as well.
Here is the code:
Form1.cs
// Global
bool boolLooper;
// Setting up the random number generator
private string RandomNumber()
{
RandomNumber rndNumber = new RandomNumber();
string strRandNumber = Convert.ToString(rndNumber.RandomInt(1000, 9999999));
return strRandNumber;
}
// TEST - used in buttonclick event
private void TextUpdates()
{
while (BoolLooper == true)
{
txtID1.Text = RandomNumber();
//txtName1.Text = RandomNumber();
//txtSize1.Text = RandomNumber();
//txtKey1.Text = RandomNumber();
//txtType1.Text = RandomNumber();
}
}
//-----------------------------
// Form - Button Clicks
//-----------------------------
// Button - Activate
private void btnActivate_Click(object sender, EventArgs e)
{
BoolLooper = true;
TextUpdates();
//// Update text once
//txtID1.Text = RandomNumber();
//txtName1.Text = RandomNumber();
//txtSize1.Text = RandomNumber();
//txtKey1.Text = RandomNumber();
//txtType1.Text = RandomNumber();
}
// Button - Stop/Deactivate
private void btnDeactivate_Click(object sender, EventArgs e)
{
BoolLooper = false;
}
//-----------------------------
// Properties
//-----------------------------
public bool BoolLooper
{
get { return boolLooper; }
set { boolLooper = value; }
}
RandomNumber.cs
private static readonly Random intRandom = new Random();
private static readonly object syncLock = new object();
public int RandomInt(int minNum, int maxNum)
{
lock (syncLock)
{
// synchronize
return intRandom.Next(minNum, maxNum);
}
}
For the RandomNumber class, I found a great post on this site found here which I will give credit to it's author: https://stackoverflow.com/a/768001
You're running this code on the same thread as the UI. Since it's single-threaded, the UI can't respond because it's busy running your loop. You'll want to off-load this to a separate thread or in some way as a separate asynchronous operation. That thread/operation would then just need to tell the UI of updates when it has them.
A simple example of this would be to use a BackgroundWorker object.
Note in the example on that page where the BackgroundWorker exposes an event which can be used to update UI elements:
private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
resultLabel.Text = (e.ProgressPercentage.ToString() + "%");
}
There are other possible approaches as well. You could create a Thread manually and try to synchronize it manually, but that comes with other potential issues as well. And there really isn't a need to get that complex here.
Do you need the TextBox to be constantly updating? Or just updating every once in a while? If there's some discernible time period between updates (one second?) then you can use a Timer to schedule the code to take place periodically. The structure is similar to the BackgroundWorker in that there's an event exposed which would be used to update the UI.
All your code is being executed on the UI thread. So you're stuck in your while loop, and the form isn't responding to the button click (which sets your while loop flag back to false). This is what we call a blocking call. It's blocking the UI from continuing.
Typically in situations like this, you would want to look into threading. However, based on your code. I'd look into a timer, and have it tick every second or so. They're very easy to implement and you can remove the complexity of your while loop and just execute the random number generation and the assigning it to your UI controls. (This also makes it so that you don't have to marshal from a background thread back onto your UI thread.)
For more information on a timer:
System.Windows.Forms.Timer
You basically need to run each call to generate a new number asynchronously. Using the .NET Framework, there are several ways to achieve that, but I prefer to use the Task class. You could do something like this:
public Task RunAsynchronously(Action method)
{
return Task.Factory.StartNew(method);
}
...
RunAsynchronously(() => MethodThatGeneratesRandomNumber());
Each time this is called, the method execution will run asynchronously.
if you are using .NET 4.5, update the TextUpdates method to use the async/await call like in the example below
private async void TextUpdates()
{
await Task.Run(() =>
{
while (BoolLooper)
{
txtID1.Invoke((MethodInvoker)(() => txtID1.Text = RandomNumber()));
//txtName1.Text = RandomNumber();
//txtSize1.Text = RandomNumber();
//txtKey1.Text = RandomNumber();
//txtType1.Text = RandomNumber();
}
});
}
You are creating new instance of a RandomNumber class each time. Just make it a member of your class. Like :
// Global
bool boolLooper;
//Random number generator
RandomNumber rndNumber = new RandomNumber();
and don't need to create new instance in method RandomNumber , just change it to this:
private string RandomNumber()
{
string strRandNumber = Convert.ToString(rndNumber.RandomInt(1000, 9999999));
return strRandNumber;
}
UPDATE: I've read a bit about Application.DoEvents() after comment, so use Invokes, await calls of Tasks, others, but not this.

Control.BeginInvoke() not working without MethodInvoker - But why?

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();
}
}

Program hangs on For loop when updating label

I have this code in a winform app:
for (int i = 1; i <= 20; i++)
{
lbl.Text = i.ToString();
Thread.Sleep(100);
}
I expected to see the label progress from 1 to 20 but instead it just hangs while the for loop is running and then displays a 20, i.e. I don't see 1-19.
Why is this and is there a way to update the label text quickly similar to the milliseconds on an analogue clock (I'm not making a clock, just an example.)
Thanks
*EDIT: This also happens if I have a button with an event that increments the number without Thread.Sleep but I have a beginInvoke to play a wav file *
You are sleeping the gui thread, so before the gui gets to update its slept again.
Try using a timer with a tick of 100ms
Call Application.DoEvents(); before sleep, that will process all messages in the queue:
for (int i = 1; i <= 20; i++)
{
lbl.Text = i.ToString();
Application.DoEvents();
Thread.Sleep(100);
}
The application hangs and you only see 20 simply because this would appear to be executing on the UI thread and you're Sleeping it, and therefore choking the window message loop. However, even if that wasn't the case, you'd most likely only ever see 20 anyway as the loop would execute so fast.
Use a System.Threading.Timer to execute every x milliseconds and update your label by invoking the appropriate method on the UI thread.
The following post demonstrates how to implement what you might want (albeit in the WPF environment, but the principle is the same - use ISynchronizeInvoke for WinForms as opposed to the Dispatcher shown in WPF here):
Timer callback closes WPF app (DispatcherTimer works..)
Use Timer control
int i=0;
private void timer_Tick(object sender, EventArgs e)
{
if(i <=20)
{
lbl.Text = i.ToString();
}
i++;
}
Set TimerInterval according to your need (Interval of 1000 = 1 second)

Pause execution of a method without locking GUI. C#

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.

Categories

Resources