I am trying to understand some code. It is a small program that prints out log data. It is done by creating a form with a DataGridView that is filled by a DataTable. The form class also has a refresh function (RefreshPresentation). The BusinessLogic class does the actual work of updating the DataTable and calling the refresh function in the form. So I pretty much understand the functionality, but not why the program is structured the way it is.
Why is businessLogic.DoWork run as a
thread instead of just a normal method call?
Can someone explain the
RefreshPresentation function for me?
(BeginInvoke and the delegate)
Is it a good idea/practice to pass the MainForm as a parameter to BusinessLogic?
This is the main entry point for the application.
public class Program
{
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new MainForm());
}
}
This is the relevant part of the form.
public partial class MainForm : Form
{
private BusinessLogic businessLogic;
private DataTable viewDataTable;
public MainForm()
{
InitializeComponent();
businessLogic = new BusinessLogic(this);
Thread t = new Thread(new ThreadStart(businessLogic.DoWork));
t.Start();
}
public delegate void RefreshPresentationDelegate(DataTable dataTable);
public void RefreshPresentation(DataTable dataTable)
{
if (this.InvokeRequired)
{
this.BeginInvoke(new RefreshPresentationDelegate(RefreshPresentation), new object[] { dataTable });
return;
}
...
This is the business logic.
internal class BusinessLogic
{
private MainForm form;
private Logging.DAL.Logger loggerDAL;
private int lastId;
internal DataTable DataTable { get; private set; }
internal bool IsRunning { get; set; }
public BusinessLogic(MainForm form)
{
this.form = form;
this.loggerDAL = new Logging.DAL.Logger();
this.IsRunning = true;
DataTable = new DataTable();
}
public void DoWork()
{
while (this.IsRunning)
{
// Get new log messages.
if (DataTable.Rows.Count > 0)
this.lastId = (int)DataTable.Rows[DataTable.Rows.Count - 1]["Id"];
this.DataTable = loggerDAL.GetLogMessagesSinceLastQuery(lastId);
// Callback to GUI for update.
form.RefreshPresentation(this.DataTable);
// Wait for next refresh.
Thread.Sleep(1000);
}
}
}
Q1.Why is businessLogic.DoWork run as a thread instead of just a normal method call?
A1. DoWork needs to be on a separate thread then the main GUI thread, since the main GUI thread needs to be free to pump the message queue (which allows it to redraw itself, handle different GUI events, etc.) Try to create simple GUI program that has a while(true) in the main thread and see that the GUI gets stuck and doesn't redraw itself.
Q2.Can someone explain the RefreshPresentation function for me? (BeginInvoke and the delegate)
A2. Though the DoWork needs to be done on another thread so it doesn't block the GUI thread, updating the GUI needs to always be done from a GUI thread. In order to make this happen, you can call BeginInvoke, which posts a message to the message queue and causes your delegate to be executed on the GUI thread.
Q3.Is it a good idea/practice to pass the MainForm as a parameter to BusinessLogic?
A3. No. The MainForm can know about the business logic, but the business logic should not be aware of the GUI. Google "MVC" for more information on separating the GUI from the business logic.
1) Looks like BusinessLogic is doing some lengthy work. To keep the UI responsive during this processing, it is executed in a different thread.
2) RefreshPresentation() is a method responsible for updating/refreshing UI while background thread is processing to keep UI up to date. Since, UI cannot be changed from a thread besides the UI thread itself, you need to use Invoke()/BeginInvoke() methods to dispatch that code to be executed on UI thread.
3) I personally believe it is a bad idea and instead an event should be exposed by BusinessLogic class to notify data change.
Related
I’m using a separate class that instantiates different barcode objects depending on the type of machine, which raise events once a barcode is read.
So to transmit it to the form, I also raise an UNIQUE event ‘myEvent’ that all my forms listens, but inside the form I have to use this.Invoke((Action) mymethod(argument)); to be able to update the components at the user interface, as long as the handler to myEvent is executed on another thread different from that of the form.
So I have this:
My class -> barcode.readed(..) is triggered, then from its handler, I Raise myEvent, that is captured on my Form, and from the handler for myEvent on my form, I execute the this.Invoke…
I know this is a somewhat standard procedure, but I’d like to get rid of that Invoke, and instead, calling directly to the myMethod(argument); function.
I think that this is related to make my class thread safe, but I don’t know how to implement that in my case.
In fact, if I use the original manufacturer barcode.readed() event from inside the form it does not need to call the invoke, as long as this is thread safe, but I don’t know how to mimic that, and I really need to wrap all the different barcode handlers inside a different project, for my forms to use only one ‘myEvent’ that returns the desired barcode, and thus, not repeating code.
Thanks in advance,
Roger Tranchez
If you inherit your class from control (basically create a new custom control), you can handle events on the UI thread without using Invokes as the control (your barcode reader class) is part of the UI thread.
Sounds like you are using a worker thread for reading barcodes, to keep the UI responsive. And the barcode object just runs on whichever thread created it.
You can centralise event handling (to avoid repeating code) and read barcodes on a worker thread as follows:
Wrap the barcode object inside a custom object/library MyBarcodeReader that exposes MyEvent.
In the constructor for MyBarcodeReader, capture the current SynchronizationContext to a class field syncContext. This will be your UI's SynchronizationContext if your form constructs MyBarcodeReader.
When you activate MyBarcodeReader (e.g. MyBarcodeReader.Execute), create the barcode object on a worker thread.
When you need to raise MyEvent, call syncContext.Send (this will be on the worker thread), passing a delegate whose purpose is to raise MyEvent. syncContext.Send will synchronise to the UI thread (like Control.Invoke). The code below illustrates this.
public class MyBarcodeReader
{
private readonly SynchronizationContext syncContext;
// Handler for barcode object's Readed event.
private void Barcode.Readed(Object sender, Event e)
{
// Block the worker thread to synchronize with the thread associated
// with SynchronizationContext.
syncContext.Send(SyncMyEvent, (Object)e);
}
// Raises MyEvent on the thread associated with SynchronizationContext,
// usually a UI thread.
private void SyncMyEvent(Object o)
{
if (MyEvent != null)
{
MyEvent((Event)o);
}
}
// Constructor.
public MyBarcodeReader()
{
syncContext = SynchronizationContext.Current;
}
}
The approach here will block the worker thread (same as Control.Invoke) but not block the UI thread. If you have one or more forms that subscribe to MyEvent, they don't need to use Control.Invoke; they don't even need to know about the worker thread.
There are some excellent online references on SynchronizationContext, see CodeProject and MSDN magazine.
I've found a solution here: Basically it passes over the form control to the class constructor, and then, inside that class it uses the form.Invoke to fire the event from the form ui thread.
CLASS:
using System;
using System.Windows.Forms;
using System.Threading;
namespace ThreadTest
{
public class WorkerClass
{
private Thread thr;
// UI control for update
public Control UIControl { get; set; }
public delegate void StatusUpdate(DateTime dateTime, string message);
public event StatusUpdate OnStatusUpdate;
// Starts thread
public void Start()
{
thr = new Thread(new ThreadStart(MainWorker));
thr.Start();
}
// Main thread worker
public void MainWorker()
{
int i = 0;
while (true)
{
string message = string.Format("Value of i={0}", i++);
FireStatusUpdate(DateTime.Now, message);
Thread.Sleep(1000);
}
}
// Fire thread safe event
private void FireStatusUpdate(DateTime dateTime, string message)
{
// UIControl is set and OnStatusUpdate has subscriber
if (UIControl != null && OnStatusUpdate != null)
{
if (UIControl.InvokeRequired)
{
UIControl.Invoke(new StatusUpdate(FireStatusUpdate),
new object[] { dateTime, message });
return;
}
OnStatusUpdate(dateTime, message);
}
}
}
}
FORM:
using System;
using System.Drawing;
using System.Windows.Forms;
namespace ThreadTest
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
WorkerClass worker = new WorkerClass();
// add event handler
worker.OnStatusUpdate += new WorkerClass.StatusUpdate(worker_OnStatusUpdate);
// add UI control to invoke
worker.UIControl = this;
worker.Start();
}
void worker_OnStatusUpdate(DateTime dateTime, string message)
{
label1.Text = dateTime.ToLongTimeString();
label1.Text += " " + message;
}
}
In my case, I've changed the type of event, from StatusUpdate to
EventHandler<MyEventArgs>
, being MyEventArgs this class:
public class MyEventArgs : EventArgs
{
public string MyString { get; set; }
}
Thank you !
I'm trying to create a class, that can be used anywhere, that runs a background worker process, which if running, will open a modal form that shows it's progress and other forms aren't usable but having difficulty working it out. Thought I might be able to do the following, but doesn't seem to work. Any other ideas?
I might be going completely the wrong way about it, but here is my cut down code:
public partial class Main : Form
{
public Main()
{
// load the database
Database.Load(this);
InitializeComponent();
}
}
namespace Blah.Common
{
public static class Database
{
private static Progress progressForm = new Progress();
public static void Load(Form parentForm)
{
bw.WorkerReportsProgress = true;
bw.WorkerSupportsCancellation = true;
bw.DoWork += (obj, e) => bw_DoWork(parentForm);
bw.RunWorkerAsync();
}
private static void bw_DoWork(Form parentForm)
{
progressForm.ShowDialog(parentForm);
progressForm.UpdateLabel("Loading...");
doHeavyProcess();
progressForm.Dispose()
}
}
}
ShowDialog() is a blocking call, the BGW will not be able to continue running until you close the dialog.
Displaying UI on a worker thread is always wrong with a long lasting effect on your program that will make it hang randomly in a very undiagnosable way. You must create the progress dialog in your UI thread, after the RunWorkerAsync() call. Update it with the ProgressChanged event.
I came across a situation which puzzled me at work today which I have simplified in the following code. This code builds and throws no exceptions during debug.
Suppose I have a WinForms app. In my main UI thread I spin off another thread which instantiates an object which in turn holds reference to a control (label1 in my example). I then call a method on my object (SetLabelText) which passes it's execution back onto the UI thread if required.
What stumped me was how, when we are back in the UI thread and executing SetLabelText, is .net CLR able to access the labelText variable when we are executing on a thread (ie the UI thread) which did not create the instance of Thing.
public partial class Form1 : Form
{
delegate void DoSomethingDelegate();
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
var t = new Thread(DoSomethingWithThing);
t.Start();
}
private void DoSomethingWithThing()
{
var thing = new Thing(this.label1);
thing.SetLabelText();
}
}
public class Thing
{
private Label label;
private string labelText = "new value";
delegate void SetLabelTextDelegate();
public Thing(Label label)
{
this.label = label;
}
public void SetLabelText()
{
if (this.label.InvokeRequired)
{
var setLabelDel = new SetLabelTextDelegate(SetLabelText);
this.label.Invoke(setLabelDel);
}
else
{
this.label.Text = this.labelText;
}
}
}
References to objects are available on any thread.
Threads are not sand-boxed from each other. They share resources unless you explicitly create non-shared resources.
Threads are execution contexts. Think of your application as a kitchen and each thread as a chef. They can work at the same time but if two of them try to use the same knife at the same time, things get messy. This is why c# has the lock keyword and other synchronization mechanisms.
WinForms has restrictions on access to controls because of the way WinForms renders.
I'm writing an application in WPF. I have one main thread and another one - where I calculate something. In main thread I need to do one operation after additional thread will be finished. I can't use Join for additional thread, because I don't want to block main thread. How I can wait for finishing second thread and at the same time don't block main thread?
The eaisest way is to use the backgroundworker and handle the RunWorkerCompleted event.
I also invite you to take a look Part 3 of
Joseph Albahari's Threading in C# pdf
Another easy way is to use Task Parallel Library and chain multiple tasks with continuations.
Though it doesn't exempt you from #Conrad's advice: Read the threading book. It's fascinating and totally worth the efforts.
If you're creating your own threads, have the worker thread invoke a callback method when it's done:
public delegate void DoneDelegate (object calculationResults);
public class MyWorker
{
public DoneDelegate Done { get; set; }
public void Go()
{
object results = null;
// do some work
Done(results);
}
}
public class Main
{
public void StartWorker()
{
MyWorker worker = new MyWorker();
worker.Done = new DoneDelegate(DoneCallback);
System.Threading.Thread thread = new System.Threading.Thread(worker.Go);
thread.IsBackground = true;
thread.Start();
}
public void DoneCallback (object results)
{
// use the results
}
}
I have a layered worker class that I'm trying to get progress reports from. What I have looks something like this:
public class Form1
{
private void Start_Click()
{
Controller controller = new Controller();
controller.RunProcess();
}
}
public class Controller
{
public void RunProcess()
{
Thread newThread = new Thread(new ThreadStart(DoEverything));
newThread.Start();
}
private void DoEverything()
{
// Commencing operation...
Class1 class1 = new Class1();
class1.DoStuff();
Class2 class2 = new Class2();
class2.DoMoreStuff();
}
}
public class Class1
{
public void DoStuff()
{
// Doing stuff
Thread.Sleep(1000);
// Want to report progress here
}
}
public class Class2
{
public void DoMoreStuff()
{
// Doing more stuff
Thread.Sleep(2000);
// Want to report progress here as well
}
}
I've used the BackgroundWorker class before, but I think I need something a bit more free form for something like this. I think I could use a delegate/event solution, but I'm not sure how to apply it here. Let's say I've got a few labels or something on Form1 that I want to be able to update with class1 and class2's progress, what's the best way to do that?
Using events is the most straightforward solution. When you subscribe to the event from the main thread, the handler should check the Control.IsInvokeRequired to know whether it must call itself again through Invoke(...) to get the message passed to the right thread.
John is correct. You want to utilize events and for that you'll need to use a delegate or delegates. This might give you some ideas.
http://www.yoda.arachsys.com/csharp/threads/winforms.shtml
If you do not want to block the processing threads during notification, you can use Control.BeginInvoke() for fire & forget behavior.
To decrease the number of calls and update progress on a regular interval, you may want to encapsulate the states of different operations in classes.
This way you can just write states to e.g. volatile fields - of presumably another, aggregate-state class - and use a timer on the GUI thread to re-read state and refresh labels accordingly.