C# Random number generator stuck in loop - c#

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.

Related

Declaring Thread in a Constructor in C#

I´m currently figuring out threads and how to work with them.
At the same time Im working on my understanding on Events/Global Events (just for context).
I defined a thread inside a object with the function the thread will use on thread.Start().
internal class Name
{
private Thread testthread;
private EventWaitHandle globalEvent;
private Eventstest evente = new Eventstest(); //Just to add some methods
public Name(Thread testthread, EventWaitHandle globalEvent)
{
this.testthread = testthread;
this.globalEvent = globalEvent;
}
public void Execute()
{
bool terminate = false;
bool eventset = false;
bool rdy = false;
while (!terminate)
{
if (evente.CheckSysEvent(globalEvent))
{
eventset = true; //This is just to check with debugger if the event was raised elsewhere
}
Thread.Sleep(100);
}
}
}
So now, like in this example in a Windows Forms App, Im trying to set an instance of this class while setting the instance of the thread at the same time (with the work method the should run later on).
Im struggling with this part here.
private void btn_runThread_Click(object sender, EventArgs e)
{
threadClass = new Name(new Thread(ProblemHere), globalEvent);
threadClass.Execute();
}
This is a button which starts the thread with the work its supposed to do.
The variable threadClass is just the initialization in the forms1.cs:
Name threadClass;
I know that it wants a delegate to pass the method which the thread should use on start.
I tried pretty much anything I found and cant make it work.
I cant just pass the method, that doesnt work.
And the stuff I found in the c# documentation is pretty much just passing the method, as far as I understood it.
Which is propably wrong.
And I just noticed, how am I able to actually call on that property/thread.start if its only created on runtime?
Not a full solution, but a bump to get you going:
What I would suggest is a little refactor like this
internal class Name
{
private Thread testthread;
private EventWaitHandle globalEvent;
private Eventstest evente = new Eventstest(); //Just to add some methods
public Name(EventWaitHandle globalEvent)
{
this.testthread = new Thread(Execute); // Creates a Thread, that is directed to execute `Execute`
this.globalEvent = globalEvent;
this.testthread.Start(); // Tells the framework to schedule the thread for execution.
}
private void Execute()
{
bool terminate = false;
bool eventset = false;
bool rdy = false;
while (!terminate)
{
if (evente.CheckSysEvent(globalEvent))
{
eventset = true; //This is just to check with debugger if the event was raised elsewhere
}
Thread.Sleep(100);
}
}
}
And in the Button handler just do
private void btn_runThread_Click(object sender, EventArgs e)
{
threadClass = new Name(globalEvent);
}
Mind that there are still a good portion of mistakes and ooopsies, but at least, this will keep your GUI thread running and you may gain an understanding to go on from here.
A totally different approach (if you are willing to consider it) would be to use a System.Windows.Forms.Timer instead. With that you can have a method called every X time, which would check the state of the globalevent as you are trying to get the thread to doing. The timer, however, makes this a little more convenient.
The typical way would be to create the thread in the constructor, as described in the answer by Fildor.
But I want to point out that using the Thread object directly is rarely the correct way to do things since there are other tools more suited for whatever you are tryibng to do:
If you want to do something compute heavy on a background thread once, and update the UI after it has been done. Use Task.Run and async/await
If you want to do something every X seconds. Use a timer. There are both timers that run on the main thread or a background thread, see differences between timers.
If you want to run an compute heavy operation in parallel, use Parallel.For, possibly in combination with Task.Run.
If you want to call IO intensive methods without freezing the UI, use async/await in combination with the appropriate Async methods.
If you want to create a producer/consumer or other processing pipeline there is the DataFlow library

Deadlock when updating a UI control from a worker thread

To simplify the explanation of the strange behavior I am experiencing, I have this simple class named Log which fires 1 log events every 1000msec.
public static class Log
{
public delegate void LogDel(string msg);
public static event LogDel logEvent;
public static void StartMessageGeneration ()
{
for (int i = 0; i < 1000; i++)
{
logEvent.Invoke(i.ToString());
Task.Delay(1000);
}
}
}
I have the Form class below which is subscribed to the log events of the Log class so it can handle them and display in a simple text box.
Once a log message arrives, it is added to a list. Every 500msec, a timer object access that list so its content can be displayed in a text box.
public partial class Form1 : Form
{
private SynchronizationContext context;
private System.Threading.Timer guiTimer = null;
private readonly object syncLock = new object();
private List<string> listOfMessages = new List<string>();
public Form1()
{
InitializeComponent();
context = SynchronizationContext.Current;
guiTimer = new System.Threading.Timer(TimerProcessor, this, 0, 500);
Log.logEvent += Log_logEvent;
}
private void Log_logEvent(string msg)
{
lock (syncLock)
listOfMessages.Add(msg);
}
private void TimerProcessor(object obj)
{
Form1 myForm = obj as Form1;
lock (myForm.syncLock)
{
if (myForm.listOfMessages.Count == 0)
return;
myForm.context.Send(new SendOrPostCallback(delegate
{
foreach (string item in myForm.listOfMessages)
myForm.textBox1.AppendText(item + "\n");
}), null);
listOfMessages.Clear();
}
}
private void button1_Click(object sender, EventArgs e)
{
Log.StartMessageGeneration();
}
}
The problem I see is that sometimes, there is a dead lock (application stuck). Seems that the 2 locks (1st one for adding to the list and the 2nd one for "retrieving" from the list) are somehow blocking each others.
Hints:
1) reducing the rate of sending the messages from 1 sec to 200msec seems to help (not sure why)
2) Somehow something happens when returning to the GUI thread (using the synchronization context) and accessing the GUI control. If I don't return to the GUI thread, the 2 locks are working fine together...
Thanks everyone!
There's a few problems with your code, and a few... silly things.
First, your Log.StartMessageGeneration doesn't actually produce a log message every second, because you're not awaiting the task returned by Task.Delay - you're basically just creating a thousand timers very quickly (and pointlessly). The log generation is limited only by the Invoke. Using Thread.Sleep is a blocking alternative to Task.Delay if you don't want to use Tasks, await etc. Of course, therein lies your biggest problem - StartMessageGeneration is not asynchronous with respect to the UI thread!
Second, there's little point in using System.Threading.Timer on your form. Instead, just use the windows forms timer - it's entirely on the UI thread so there's no need for marshalling your code back to the UI thread. Since your TimerProcessor doesn't do any CPU work and it only blocks for a very short time, it's the more straight-forward solution.
If you decide to keep using System.Threading.Timer anyway, there's no point in manually dealing with synchronization contexts - just use BeginInvoke on the form; the same way, there's no point in passing the form as an argument to the method, since the method isn't static. this is your form. You can actually see this is the case since you omitted myForm in listOfMessages.Clear() - the two instances are the same, myForm is superfluous.
A simple pause in the debugger will easily tell you where the program is hung - learn to use the debugger well, and it will save you a lot of time. But let's just look at this logically. StartMessageGeneration runs on the UI thread, while System.Threading.Timer uses a thread-pool thread. When the timer locks syncLock, StartMessageGeneration can't enter the same lock, of course - that's fine. But then you Send to the UI thread, and... the UI thread can't do anything, since it's blocked by StartMessageGeneration, which never gives the UI an opportunity to do anything. And StartMessageGeneration can't proceed, because it's waiting on the lock. The only case where this "works" is when StartMessageGeneration runs fast enough to complete before your timer fires (thus freeing the UI thread to do its work) - which is very much possible due to your incorrect use of Task.Delay.
Now let's look on your "hints" with all we know. 1) is simply your bias in measurements. Since you never wait on the Task.Delay in any way, changing the interval does absolutely nothing (with a tiny change in case the delay is zero). 2) of course - that's where your deadlock is. Two pieces of code that depend on a shared resource, while they both require to take posession of another resource. It's a very typical case of a deadlock. Thread 1 is waiting for A to release B, and thread 2 is waiting for B to release A (in this case, A being syncLock and B being the UI thread). When you remove the Send (or replace it with Post), thread 1 no longer has to wait on B, and the deadlock disappears.
There's other things that make writing code like this simpler. There's little point in declaring your own delegate when you can just use Action<string>, for example; using await helps quite a bit when dealing with mixed UI/non-UI code, as well as managing any kind of asynchronous code. You don't need to use event where a simple function will suffice - you can just pass that delegate to a function that needs it if that makes sense, and it may make perfect sense not to allow multiple event handlers to be called. If you decide to keep with the event, at least make sure it conforms to the EventHandler delegate.
To show how your code can be rewritten to be a bit more up-to-date and actually work:
void Main()
{
Application.Run(new LogForm());
}
public static class Log
{
public static async Task GenerateMessagesAsync(Action<string> logEvent,
CancellationToken cancel)
{
for (int i = 0; i < 1000; i++)
{
cancel.ThrowIfCancellationRequested();
logEvent(i.ToString());
await Task.Delay(1000, cancel);
}
}
}
public partial class LogForm : Form
{
private readonly List<string> messages;
private readonly Button btnStart;
private readonly Button btnStop;
private readonly TextBox tbxLog;
private readonly System.Windows.Forms.Timer timer;
public LogForm()
{
messages = new List<string>();
btnStart = new Button { Text = "Start" };
btnStart.Click += btnStart_Click;
Controls.Add(btnStart);
btnStop =
new Button { Text = "Stop", Location = new Point(80, 0), Enabled = false };
Controls.Add(btnStop);
tbxLog = new TextBox { Height = 200, Multiline = true, Dock = DockStyle.Bottom };
Controls.Add(tbxLog);
timer = new System.Windows.Forms.Timer { Interval = 500 };
timer.Tick += TimerProcessor;
timer.Start();
}
private void TimerProcessor(object sender, EventArgs e)
{
foreach (var message in messages)
{
tbxLog.AppendText(message + Environment.NewLine);
}
messages.Clear();
}
private async void btnStart_Click(object sender, EventArgs e)
{
btnStart.Enabled = false;
var cts = new CancellationTokenSource();
EventHandler stopAction = (_, __) => cts.Cancel();
btnStop.Click += stopAction;
btnStop.Enabled = true;
try
{
await Log.GenerateMessagesAsync(message => messages.Add(message), cts.Token);
}
catch (TaskCanceledException)
{
messages.Add("Cancelled.");
}
finally
{
btnStart.Enabled = true;
btnStop.Click -= stopAction;
btnStop.Enabled = false;
}
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
timer.Dispose();
btnStart.Dispose();
btnStop.Dispose();
tbxLog.Dispose();
}
base.Dispose(disposing);
}
}
SynchronizationContext.Send is run synchronously. When you call it, you actually block the UI thread until the operation is complete. But if UI thread is already in lock state, then it just make sense that you are in deadlock.
You can use SynchronizationContext.Post to avoid this.
I just answer on your question, but the truth is that your code need a "little" refactoring..

Why is UI unresponsive while being programmatically controlled?

I hand-rolled a MVC-style implementation of a game that I want to autoplay. By "autoplay" I mean that the buttons that normally a user would click while playing I want a controller to automatically initiate. That way I can watch the game play itself for quality control reasons. This particular game has a lot of code, so instead of providing it as an example I've created a silly HelloWorld example using the same approach.
Before I provide the example, here is my issue: everything you see below is functional, and "works"; except for one thing: I'm unable to shut-off the autoplay because the UI becomes unresponsive and the button to turn it off won't respond to a click event.
First create a .Net 4.6.1 winforms project in a solution. (.net version probably doesn't matter as long as it is >= 4.5). Create a Form that looks like this:
In the code behind, copy paste this: (change names as needed to compile)
using System;
using System.Threading;
using System.Windows.Forms;
namespace WinformsExample
{
public partial class HelloWorldView : Form
{
private readonly HelloWorldController MyHelloWorldController;
public HelloWorldView()
{
InitializeComponent();
MyHelloWorldController = new HelloWorldController();
}
private void button1_Click(object sender, EventArgs e)
{
MyHelloWorldController.HelloWorldRequested();
if (MyHelloWorldController.IsAutomated)
{
Thread.Sleep(2000);
button1.PerformClick();
}
}
private void HelloWorldView_Load(object sender, EventArgs e)
{
MyHelloWorldController.HelloWorldRequestedEvent += OnHelloWorldRequested;
}
private void OnHelloWorldRequested(HelloWorldParameters parameters)
{
textBox1.Text += parameters.HelloWorldString + Environment.NewLine;
textBox1.Update();
}
private void button2_Click(object sender, EventArgs e)
{
MyHelloWorldController.IsAutomated = !MyHelloWorldController.IsAutomated;
if (MyHelloWorldController.IsAutomated)
{
button2.Text = "hello world - is on";
button2.Update();
button1.PerformClick();
}
else
{
button2.Text = "hello world - is off";
button2.Update();
}
}
}
}
And create a class titled HelloWorldController.cs and copy paste this in to it:
namespace WinformsExample
{
public class HelloWorldParameters
{
public string HelloWorldString { get; set; }
}
public delegate void HelloWorldEventHandler(HelloWorldParameters parameters);
public class HelloWorldController
{
private readonly HelloWorldParameters _parameters;
public event HelloWorldEventHandler HelloWorldRequestedEvent;
public bool IsAutomated { get; set; }
public HelloWorldController()
{
_parameters = new HelloWorldParameters();
}
public void HelloWorldRequested()
{
_parameters.HelloWorldString = "Hello world!!";
if (HelloWorldRequestedEvent != null)
HelloWorldRequestedEvent(_parameters);
}
}
}
...go ahead and rename things if you need to. Now build the program. Click the first button. You will see "hello world". Now click the second button, you will see "hello world" printed every 2 seconds.
The way I thought this would work is that by clicking button2 a second time, that it would stop the autoplay. However, the UI is unresponsive and the button click event never happens.
What is going on here that is causing the UI to be unresponsive and how can I fix it so that I get the intended behavior?
*UPDATE - HERE IS THE SOLUTION *
Keep everything the same as above except for HelloWorldView.cs. Remove the call to Thread.Sleep(). Drag and drop a timer from the toolbox to the design surface. You will see an icon on the bottom of the designer surface labeled
timer1
Copy paste the following code in to HelloWorldView.cs. Compile and execute. If everything is correct you should be able to turn on and off the "hello world" display by clicking the button at any time - the UI stays responsive.
using System;
using System.Windows.Forms;
namespace WinformsExample
{
public partial class HelloWorldView : Form
{
private readonly HelloWorldController MyHelloWorldController;
public HelloWorldView()
{
InitializeComponent();
MyHelloWorldController = new HelloWorldController();
}
private void onTimerTick(object sender, EventArgs e)
{
button1.PerformClick();
}
private void OnHelloWorldRequested(HelloWorldParameters parameters)
{
textBox1.Text += parameters.HelloWorldString + Environment.NewLine;
textBox1.Update();
}
private void HelloWorldView_Load(object sender, EventArgs e)
{
MyHelloWorldController.HelloWorldRequestedEvent += OnHelloWorldRequested;
}
private void button1_Click(object sender, EventArgs e)
{
MyHelloWorldController.HelloWorldRequested();
}
private void button2_Click(object sender, EventArgs e)
{
MyHelloWorldController.IsAutomated = !MyHelloWorldController.IsAutomated;
if (MyHelloWorldController.IsAutomated)
{
button2.Text = "hello world - is on";
button2.Update();
timer1.Interval = 2000;
timer1.Tick += onTimerTick;
timer1.Start();
}
else
{
timer1.Stop();
button2.Text = "hello world - is off";
button2.Update();
}
}
}
}
WinForms uses a single message pump thread (called the UI thread). (If you are unfamiliar with the concept you should research Windows messages and Windows message pump).
Thread.Sleep causes the currently executing thread the sleep, or pause, for a time. This sleep/pause is like death to the thread - it is aware of nothing and unable to do anything.
As the currently executing thread in a WinForms app is usually the UI thread - Thread.Sleep will cause the UI to become unresponsive because it is no longer able to pump messages.
An alternative design would be to use a form-based Timer. Place your game playing code in the Timer's Tick event.
What is going on here that is causing the UI to be unresponsive and how can I fix it so that I get the intended behavior?
There are essentially two reasons why your app becomes unresponsive.
1. Thread.Sleep() in UI thread
GUI applications on Windows are generally driven by messages (mouse clicks; keyboard; screen drawing) posted to it which are placed on a queue. The UI thread processes these messages one by one dispatching the message to the appropriate handler. In this way it is known as the Message Pump. If during processing one of these messages too much time elapses, then the UI will appear to freeze. Event handlers should be as fast as possible.
During your click handlers you are using Thread.Sleep(2000); which will prevent the UI thread from updating the UI of your application, in essence simulating an event handler that takes far too long to process an event. It is perhaps no different to say performing a lengthy database or WCF operation on the UI thread, hence why people tend to put such calls on a separate thread or task.
Recommend you remove the Thread.Sleep and replace it with a timer as others have indicated.
2. Infinite Recursive Loop on button1 handler
When you click button2 for the first time, the click handler for button2 is invoked where automation is enabled. You then simulate button1 being clicked via button1.PerformClick();.
During the call to button1.PerformClick, the click handler for button1 button1_Click() is invoked. It is there that you sleep for 2 seconds (which isn't healthy for the UI) but the secondary problem is that you immediately call button1.PerformClick(); from inside the button1 click handler, in essence setting up an infinite recursive loop.
If you were to remove the Thread.Sleep(2000) your app will eventually lead to a StackOverflowException. Your code as it stands now (even with the sleep) will still overflow, it's just that it will take much longer to become apparent.
Again, consider replacing it with a timer.
3. Exclusivity
It's important to note that ignoring the stack fault for a moment, the design is such that your app can't do anything else whilst this infinite loop is running. So if your game had other buttons to click; scores to display; sound effects to play; all from the point of view of the button2 handler, most likely it will never happen because it is too busy exclusively processing button1.
Conclusion
Keep UI responsive: Avoid Thread.Sleep() in your code
Avoid recursion: Don't use PerformClick() for a button whilst you are inside the click handler for said button
Your "Thread.Sleep()" call puts the UI thread to sleep. Use a Timer instead. Then terminate the Timer on the second press. (You could also do this with Tasks, if you want to use another thread you need to make the 2 threads communicate in someway so that the UI thread is the only one actually updating the UI)
Desktop applications have a so called UI thread. It's basically an infinite loop which keeps checking if something happened, such as a mouse click, and redraws the window if needed. Coding in WinAPI you would need to write this loop yourself, WinForms and other UI frameworks hide it away. But your click handler is called from inside this loop. So if your code takes too much time - like, because you call Thread.Sleep inside - the loop will not continue and will not be able to process anything that is happening to the application. This why long-running processes need to take place on a separate thread.
As others have said, you are blocking the UI thread with the Thread.Sleep and recursive button1.PerformClick(); call. You have to let the UI run as freely as possible and let it go idle quickly.
So, just for the fun of it I have rewritten your code to do just that. I've also implemented it with Microsoft's Reactive Extensions (Rx) - just NuGet "Rx-WinForms" to get the bits. Rx allows you to do some very funky things that you can't easily do with events.
Here's your form now:
public partial class HelloWorldView : Form
{
private readonly HelloWorldController MyHelloWorldController =
new HelloWorldController("Hello world!!", TimeSpan.FromSeconds(1.0));
public HelloWorldView()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
MyHelloWorldController.Messages
.ObserveOn(this)
.Subscribe(message =>
{
textBox1.Text += message + Environment.NewLine;
});
MyHelloWorldController.IsAutomateds
.ObserveOn(this)
.Subscribe(isAutomated =>
{
button2.Text = "hello world - is " + (isAutomated ? "on" : "off");
});
}
private void button1_Click(object sender, EventArgs e)
{
MyHelloWorldController.Trigger();
}
private void button2_Click(object sender, EventArgs e)
{
MyHelloWorldController.IsAutomated = !MyHelloWorldController.IsAutomated;
}
}
You'll notice that I've simplified down the UI. It really does as little as possible to update itself and to notify the HelloWorldController of its actions.
The worst part of the code are the two .Subscribe calls in Form1_Load. These are simply looking at the two observables (Rx's version of events if you like) and makes sure the events are run on the UI thread with the .ObserveOn(this) call, and then they subscribe to values produced from the HelloWorldController.
The UI is simply updating itself from the controller and telling the controller what it is doing. There is virtually no logic being performed in the UI. This is how it should be with any MVC-style coding.
Now the HelloWorldController is where the fun is.
It starts off pretty simply:
private string _message;
private TimeSpan _automatedPeriod;
public HelloWorldController(string Message, TimeSpan automatedPeriod)
{
_message = Message;
_automatedPeriod = automatedPeriod;
}
This is basically the information about what message to send to the UI and how often when the controller is automating the values.
It then tracks whether it is automated or not:
private bool _isAutomated = false;
Now it contains the Rx observables - these are like the events you were using.
private Subject<string> _messages = new Subject<string>();
public IObservable<string> Messages { get { return _messages.AsObservable(); } }
private Subject<bool> _isAutomateds = new Subject<bool>();
public IObservable<bool> IsAutomateds { get { return _isAutomateds.AsObservable(); } }
private SerialDisposable _serialSubscription = new SerialDisposable();
In Rx an IObservable<T> is something I can subscribe to to get a series of values - just like an event. The Subject<T> is something that I can manually push values into, but it also can be an IObservable<T> that can be subscribed to. It's the pair of these that lets me raise events. Think of the Subject<string> to be the equivalent of the HelloWorldRequested method in your code and the IObservable<string> to be the equivalent of the HelloWorldRequestedEvent event.
If I call _messages.OnNext("Hello") then any subscribers to IObservable<string> Messages would get a "Hello" sent to them. Just like an event.
IsAutomated looks like this:
public bool IsAutomated
{
get { return _isAutomated; }
set
{
_isAutomated = value;
_isAutomateds.OnNext(value);
if (_isAutomated)
{
this.Trigger();
}
}
}
So it does its job of updating its own internal state, but it also calls _isAutomateds.OnNext(value) to push out the updates to any subscribers of IObservable<bool> IsAutomateds. It also works out if it needs to trigger the controller to produce messages with the this.Trigger() call.
Finally the Trigger method looks like this:
public void Trigger()
{
if (_isAutomated)
{
_serialSubscription.Disposable =
Observable
.Interval(_automatedPeriod)
.StartWith(0)
.TakeUntil(_isAutomateds.Where(x => x == false))
.Subscribe(n => _messages.OnNext(_message));
}
else
{
_messages.OnNext(_message);
}
}
The easy part of this is when the _isAutomated is false then it simply sends one message out via the _messages.OnNext(_message) call.
When _isAutomated is true it uses some of the coolness of Rx to set up effectively a timer to produce values every TimeSpan _automatedPeriod. From your code you wanted every 2 seconds so the TimeSpan would be TimeSpan.FromSeconds(2.0).
Observable.Interval(_automatedPeriod) defines a timer that begins producing values after the first period of time and then every period of time between.
So the .StartWith(0) says that it should immediately produce a value when it is subscribed to.
The .TakeUntil(_isAutomateds.Where(x => x == false)) is the best part here - it says that it will take the values from the the Observable.Interval(_automatedPeriod).StartWith(0) and stop when it gets a value from _isAutomateds.Where(x => x == false) - in other words when the IsAutomated is set to false.
The .Subscribe(n => _messages.OnNext(_message)); simply pushes a value to the _messages subject so that all subscribers of IObservable<string> Messages gets their messages.
Just put all of the HelloWorldController I've given you in public class HelloWorldController { ... } and you're good to go.
The works I think like it should and shows how lightweight the UI code can be.
I hope you find this worth playing with.
You'll need to add these using's to the top of your code to get all of the code to compile:
using System.Reactive.Disposables;
using System.Reactive.Linq;
using System.Reactive.Subjects;

C# Invoke button control on separate thread

I have seen a lot of questions about how to edit controls on c# form from a different thread but none make much sense to me. I understand that you can not change any UI from another thread than it's main. To make this work you have to use invoke and from there safely edit the control?
I have a button that starts writing in a file and the moment you press the button the button itself gets disabled so you can not start multiple threads that do exactly the same. When the writing is done I want the button to be available again but I can not get it working on this other thread.
I have this as the Generate_Click event from the form.
private void Generate_Click(object sender, EventArgs e)
{
Generate.Enabled = false;
int x = 512;
int y = 512;
MBrot mbrot = new MBrot(x, y);
PB_Update lb = new PB_Update(0, y, Generator_PB, Generate, mbrot, this);
lb.Start();
}
And this is in PB_Update.cs the ThreadWork() function, when the while loop is done the writing to the file is done and so is the thread so its ended and given a messagebox with "finished" now as last the button needs to be enabled again.
public void ThreadWork()
{
while (true)
{
if (currValue_ >= maxValue_)
break;
ThreadTick();
}
mb_.StopBrot();
t_.Interrupt();
MessageBox.Show("Finished!");
Generate_.Enabled = true;
}
For WinForms you can execute directly on the thread which the control was created on through the Control.BeginInvoke method, you can use Control.Invoke as well but, Control.BeginInvoke is preferred for UI operations.
public void ThreadWork()
{
while (true)
{
if (currValue_ >= maxValue_)
break;
ThreadTick();
}
mb_.StopBrot();
t_.Interrupt();
MessageBox.Show("Finished!");
Generate_.BeginInvoke((Action)delegate()
{
Generate_.Enabled = true;
});
}
Somehow, get a reference to the form that hosts the generate_ button (let's call it myform). Then, at the bottom of your ThreadWork:
myform.Invoke(new Action(() => {
myform.SetGenerateEnabled();
}));
And then inside your form create that method that enables the button appropriately. (I used a method rather than just updating the button directly so that you don't publicly expose the button.)
This executes the commands inside the { ... } on myform's thread, which is a UI thread, because it is UI. At least, that's what I understand. This is how I do all of my UI updating from other threads.
Here's a simple example of a way to kick off an async task that disables a button for 5 seconds and then enables it again. Meanwhile, the rest of the UI is functional.
Note that this async method exists in the same class as your Generate_Click event, and runs on the UI thread. This means that it can enable and disable the button. But the long running task executes on a separate thread, so it doesn't lock the UI.
Hopefully this sample provides you a base to modify for your own code:
private void Generate_Click(object sender, EventArgs e)
{
DisableButton(sender as Button, 5);
}
private async void DisableButton(Button sender, int secondsToDisable)
{
sender.Enabled = false;
// In your code, you would kick off your long-running process here as a task
await Task.Run(()=>Thread.Sleep(TimeSpan.FromSeconds(secondsToDisable)));
sender.Enabled = true;
}

How to use a BackgroundWorker to update multiple labels?

This is a follow up question to Updating a dialog from another form (The code and screenshots can be found there)
To solve my GUI hanging problem I received 2 recommendations:
Using Application.DoEvents()
Using a BackgroundWorker
The DoEvents() approach works, however it has been pointed out that I should not use it. Indeed, I notice that the GUI updates correctly but is unresponsive for short times.
That's why I want to use a BackgroundWorker and have read up on it.
I don't understand how I would implement it so that it can be used to update the 4 labels in my example code separately, though.
I want to show the progress (and update 4 dialog labels) as the program successfully finishes one job. The BackgroundWorker has only 1 DoWork() though. I have tried to use the e.Argument of the DoWorkEventArgs to differentiate between the different update methods but that attempt had failed.
public partial class BackgroundWorkerImportStatusDialog : Form
{
private BackgroundWorker dialogWorker = new BackgroundWorker();
private string path;
private string clientName;
public BackgroundWorkerImportStatusDialog()
{
InitializeComponent();
}
public void updateFileStatus(string path)
{
this.path = path;
dialogWorker = new BackgroundWorker();
dialogWorker.DoWork += new DoWorkEventHandler(updateLabels);
dialogWorker.RunWorkerAsync(UpdateComponent.FileStatus);
}
public void updatePrintStatus()
{
dialogWorker = new BackgroundWorker();
dialogWorker.DoWork += new DoWorkEventHandler(updateLabels);
dialogWorker.RunWorkerAsync(UpdateComponent.PrintStatus);
}
public void updateImportStatus(string clientName)
{
this.clientName = clientName;
dialogWorker = new BackgroundWorker();
dialogWorker.DoWork += new DoWorkEventHandler(updateLabels);
dialogWorker.RunWorkerAsync(UpdateComponent.ImportStatus);
}
public void updateArchiveStatus()
{
dialogWorker = new BackgroundWorker();
dialogWorker.DoWork += new DoWorkEventHandler(updateLabels);
dialogWorker.RunWorkerAsync(UpdateComponent.ArchiveStatus);
}
private void updateLabels(object sender, DoWorkEventArgs e)
{
MessageBox.Show(e.Argument.ToString());
if ((UpdateComponent) e.Argument == UpdateComponent.FileStatus)
{
t_filename.Text = path;
}
if ((UpdateComponent) e.Argument == UpdateComponent.PrintStatus)
{
t_printed.Text = "sent to printer";
}
if ((UpdateComponent) e.Argument == UpdateComponent.ImportStatus)
{
t_client.Text = clientName;
}
if ((UpdateComponent) e.Argument == UpdateComponent.ArchiveStatus)
{
t_archived.Text = "archived";
}
}
public enum UpdateComponent { FileStatus, PrintStatus, ImportStatus, ArchiveStatus}
And I can't imagine having 4 BackgroundWorkers for this pretty trivial dialog is the solution.
As I understand your question, you want to have your dialog form inform the user about 4 different aspects of your application running:
printing status
file status
import status
archiver status
Background worker could be used to periodically check each one. You may advanced progressbar by 25% after status of each operation is checked (and update your UI with appropriate information).
You may also try async programming - i.e. just start the operation, and lets your application continue. When the operation completes, your application will be notified, and could update information on the form.
Depending on the .NET framework you're using you may use async and await (avaialble since .NET 4.5 / C# 5 - async & await on MSDN) or classic approach to asynchronous programming.
Edit:
I am not sure that BackgroundWorker is the best solution in this situation. I can imagine having something like:
BackhgroundWorker checking things just once - i.e. check printing status once, file status once, import status once, archiver status once. This may sound silly, but it could be user behavior driver - i.e. explicitly launched when user clicks or invokes this mechanism any other way. ProgressBar could be put on the application's statausbar, so that user knows that 'application is actually doing something'.
Previous approach could be improved a bit - you never actually finish your job in BackgroundWorker - instead inside your main method you just have an infinite loop. This will allow you to check things periodically. In this approach there is no point in increasing the progress.
Sample for the second approach:
private void bg_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker worker = sender as BackgroundWorker;
for (int i = 1; i <= 10; i++)
{
if (worker.CancellationPending == true)
{
e.Cancel = true;
break;
}
else
{
CheckPrintingStatus();
CheckFileStatus();
CheckImportStatus();
CheckArchiverStatus();
System.Threading.Thread.Sleep(5000); // sleep for 5 seconds
}
}
}
There is a question if this solution (second approach) is better than having a thread created explicitly. You could think of creating 4 different threads, so that each could check something else. This would be a bit heavier on the OS, but on the other hand you can set different sleep times for every operation.
If you go for bare threads - you may want to use ThreadPool instead of creating threads explicitly.

Categories

Resources