Currently, I am using a thread to autofill a zedgraph chart. The problem is that it is very slow and that is not good for the production.
Knowing that:
1. There is a Thread running background to get fresh data from remote server and store them in a local text file.
2. There is another Thread accesses and reads data from the local file and refresh the zedgraph chart.
Personnaly I think this makes the application running very slowly.
Timer component may be better to handle this kind of stuff.
Does anyone has experience in ZedGraph Live Data?
Or any advice would be welcomed.
EDIT: UPDATE UI
Also I would like to know how to update the UI within a Thread so that the user does not have to close and open the form to view the chart.
Thanks
The way I would go about this is as follows:
You make a class that holds the data (this can also be the same class you already have) for the example I will call it InformationHolder.
class InformationHolder {
private static InformationHolder singleton = null;
public List<float> graphData; //Can be any kind of variable (List<GraphInfo>, GraphInfo[]), whatever you like best.
public static InformationHolder Instance() {
if (singleton == null) {
singleton = new InformationHolder();
}
return singleton;
}
}
Now you need a class that will get your information in the background, parses it and inserts it into the above class.
Example with Thread.Sleep():
class InformationGartherer {
private Thread garthererThread;
public InformationGartherer() {
garthererThread = new Thread(new ThreadStart(GartherData));
}
private void GartherData() {
while (true) {
List<float> gartheredInfo = new List<float>();
//Do your garthering and parsing here (and put it in the gartheredInfo variable)
InformationHolder.Instance().graphData = gartheredInfo;
graphForm.Invoke(new MethodInvoker( //you need to have a reference to the form
delegate {
graphForm.Invalidate(); //or another method that redraws the graph
}));
Thread.Sleep(100); //Time in ms
}
}
}
Example with Timer:
class InformationGartherer {
private Thread garthererThread;
private Timer gartherTimer;
public InformationGartherer() {
//calling the GartherData method manually to get the first info asap.
garthererThread = new Thread(new ThreadStart(GartherData));
gartherTimer = new Timer(100); // time in ms
gartherTimer.Elapsed += new ElapsedEventHandler(TimerCallback);
gartherTimer.Start();
}
private void TimerCallback(object source, ElapsedEventArgs e) {
gartherThread = new Thread(new ThreadStart(GartherData));
}
private void GartherData() {
List<float> gartheredInfo = new List<float>();
//Do your garthering and parsing here (and put it in the gartheredInfo variable)
InformationHolder.Instance().graphData = gartheredInfo;
graphForm.Invoke(new MethodInvoker( //you need to have a reference to the form
delegate {
graphForm.Invalidate(); //or another method that redraws the graph
}));
}
}
To get the reference to the form you could do the same trick as I used with the InformationHolder: using a singleton.
When you want to use the information just get it from the InformationHolder like so:
InformationHolder.Instance().graphData;
As I said this is how I personally would solve this. There might be a better solution I'm not aware of. If you have any questions you can post them below.
Related
I am trying to update a Windows form listbox from a different thread in C#.
I have a button that creates a new thread. This thread calls a method in another class that starts an asynchronous function which populates a list data type.
The contents of the list needs to be set as the contents of the list box each time a new item is added to the list.
Updating the list box, in the same thread it was created, is a problem because of the need to update with each new item; using a loop will make the rest of the form controls unresponsive.
I have tried using
this.Invoke((MethodInvoker)(() => OutputBox.Items.Add(engineOutput)));
found
How to add item to listBox from other thread?,
but I haven't been able to get this to work. I am not sure if I am have it in the wrong place or it is not relevant to my solution. (The line above has been changed to fit the context of the solution and is shown below in the code snippet).
I have added a simplified version of code that shows where I have tried to implement the above solution and how the a visual explanation of the description above.
public partial class FrmPacketSniffer : Form
{
protected bool stop = false;
public FrmPacketSniffer()
{
InitializeComponent();
}
private void btnStart_Click(object sender, EventArgs e)
{
ThreadStart startSniff = new ThreadStart(StartSniffing);
Thread startThread = new Thread(startSniff);
startThread.Start();
}
private void StartSniffing()
{
PacketSniffng sniffer = new PacketSniffing;
sniffer.Connection(this);
}
}
class PacketSniffing
{
public static bool pause = false;
static List<string> headerList = new List<string>();
public static void Connection(FrmPacketSniffer frmPacketSniffer)
{
//Creates a socket called con...
Sniffing(con, frmPacketSniffer);
}
private void Sniffing(Socket con, FrmPacketSniffer frmPacketSniffer)
{
Action<IAsyncResult> collect = null;
collect = (ar)=>
{
headerList.Add("data");
//this.Invoke((MethodInvoker)(() => frmPacketSniffer.lstPackets.Items.Add(headerList)));
//Here I placed the code mentioned above.
frmPacketSniffer.lstPackets.DataSource = headerList; //List box 'lstPackets' is the target to update.
con.BeginReceive(packet, 0, 24, SocketFlags.None, new AsyncCallback(collect), null);
};
}
}
Can anyone provide any tips on how I can fix this please?
Thanks
You need to pass the instance of your form to your instance of PacketSniffing when you create it, and then use that instance and call Invoke on it.
Fairly frustrating since this seems to be well documented and the fact that I accomplished this before, but can't duplicate the same success. Sorry, I'll try to relate it all clearly.
Visual Studio, C# Form, One Main Form has text fields, among other widgets.
At one point we have the concept that we are "running" and therefore gathering data.
For the moment, I started a one second timer so that I can update simulated data into some fields. Eventually that one second timer will take the more rapid data and update it only once per second to the screen, that's the request for the application right now we update at the rate we receive which is a little over 70 Hz, they don't want it that way. In addition some other statistics will be computed and those should be the field updates. Therefore being simple I'm trying to just generate random data and update those fields at the 1 Hz rate. And then expand from that point.
Definition and management of the timer: (this is all within the same class MainScreen)
System.Timers.Timer oneSecondTimer;
public UInt32 run_time = 0;
public int motion = 5;
private void InitializeTimers()
{
this.oneSecondTimer = new System.Timers.Timer(1000);
this.oneSecondTimer.Elapsed += new System.Timers.ElapsedEventHandler(oneSecondTimer_elapsed);
}
public void start_one_second_timer()
{
run_time = 0;
oneSecondTimer.Enabled = true;
}
public void stop_one_second_timer()
{
oneSecondTimer.Enabled = false;
run_time = 0;
}
Random mot = new Random();
private void oneSecondTimer_elapsed(object source, System.Timers.ElapsedEventArgs e)
{
run_time++;
motion = mot.Next(1, 10);
this.oneSecondThread = new Thread(new ThreadStart(this.UpdateTextFields));
this.oneSecondThread.Start();
}
private void UpdateTextFields()
{
this.motionDisplay.Text = this.motion.ToString();
}
motionDisplay is just a textbox in my main form. I get the Invalid Operation Exception pointing me towards the help on how to make Thread-Safe calls. I also tried backgroundworker and end up with the same result. The details are that motionDisplay is accessed from a thread other than the thread it was created on.
So looking for some suggestions as to where my mistakes are.
Best Regards. I continue to iterate on this and will update if I find a solution.
Use a System.Forms.Timer rather than a System.Timers.Timer. It will fire it's elapsed event in the UI thread.
Don't create a new thread to update the UI; just do the update in the elapsed event handler.
Try this
private void UpdateTextFields()
{
this.BeginInvoke(new EventHandler((s,e)=>{
this.motionDisplay.Text = this.motion.ToString();
}));
}
This will properly marshall a call back to the main thread.
The thing with WinForm development is that all the controls are not thread safe. Even getting a property such as .Text from another thread can cause these type of errors to happen. To make it even more frustrating is that sometimes it will work at runtime and you won't get an exception, other times you will.
This is how I do it:
private delegate void UpdateMotionDisplayCallback(string text);
private void UpdateMotionDisplay(string text) {
// InvokeRequired required compares the thread ID of the
// calling thread to the thread ID of the creating thread.
// If these threads are different, it returns true.
if (this.motionDisplay.InvokeRequired) {
UpdateMotionDisplayCallback d = new UpdateMotionDisplayCallback(UpdateMotionDisplay);
this.Invoke(d, new object[] { text });
} else {
this.motionDisplay.Text = text;
}
}
When you want to update the text in motionDisplay just call:
UpdateMotionDisplay(this.motion.ToString())
I need to use controls on another thread. I know that i need to invoke them but dont know how. This is my code:
Thread threadWriteLog = new Thread(new ThreadStart(this.WriteLog));
threadWriteLog.Start();
private void WriteLog()
{
date = DateTime.Now;
using (StreamWriter swLog = new StreamWriter(String.Format("{0}\\RoutesLogs\\{1}.log", Settings.Instance.Paths.SDCard, textName), true)) //zapisovanie logu
{
if (btnStartPause.Text == "Start Recording")
swLog.WriteLine(String.Format("Route start: {0}", date.ToString(format)));
else if (btnStartPause.Text == "Pause Recording")
swLog.WriteLine(String.Format("Route pause: {0}", date.ToString(format)));
else if (btnStartPause.Text == "Resume Recording")
swLog.WriteLine(String.Format("Route resume: {0}", date.ToString(format)));
}
}
Can u write me a solve code?
Rather than having the other thread marshal back to the UI thread just to read data from a control, pull the string text out of the control before string the thread and then provide that string to the new thread when creating it. The easiest way of doing this is through a lambda that closes over the information:
string text = control.Text;
Thread thread = new Thread(() => WriteLog(text));
thread.Start();
Then just add a string parameter to WriteLog for the data. You can do this for each piece of information needed.
Beyond simply preventing cross thread exception errors, a key aspect of this design is that you have now separated your business logic from your user interface, which makes the application much easier to maintain going forward.
Simple answer: I think the .Text Methods should work without invocation.
Have you already tried it out?
[Edit]
Here is an short example how to do the invocation:
public class Dlg
{
public delegate void UpdateConnLabel(string txt);
private event UpdateConnLabel _UpdateConnLabel;
public Dlg()
{
InitializeComponent();
_UpdateConnLabel = new UpdateConnLabel(DoUpdateConnectionLabel);
}
public void UpdateConnectionLabel(String txt)
{
this.Invoke(_UpdateConnLabel, new object[] { txt });
}
private void DoUpdateConnectionLabel(string txt)
{
label_connection.Text = txt;
}
}
You simply call UpdateConnectionLabel("hello World"); everywhere you want to update the text on the label.
I hope this helps to understand the mechanism.
Ok, well I have been at it for a while now and I decided to just use threads. I am making a syntax highlighter but I keep getting terrible performance with the file sizes that it will usually be used for. So I made two forms, the first shows the file in plain text and has a button that says "openincolor" when you click that I start a new thread as such
private void button1_Click(object sender, EventArgs e)
{
ColoringThread colorer = new ColoringThread(this.m_bruteView.Text);
Thread theThread = new Thread(new ThreadStart(colorer.OpenColorWindow));
theThread.Start();
}
public class ColoringThread
{
string text;
public ColoringThread(string initText)
{
text = initText;
}
public void OpenColorWindow()
{
Form2 form2 = new Form2(text);
form2.ShowDialog();
}
};
I want this form to send back a message each time it has complete say x lines of coloring. Then I will take that and figure out the progress and display it to the user.
How might I go about sending a message, or event(...? can I do that) to my first form to let it know of the others progress?
One very simple way to do this is with BackgroundWorker. It already provides an event to report progress.
How about something like this? This adds an event to the ColoringThread class which is subscribed to by the calling class.
private void button1_Click(object sender, EventArgs e) {
ColoringThread colorer = new ColoringThread(this.m_bruteView.Text);
colorer.HighlightProgressChanged += UpdateProgress;
Thread theThread = new Thread(new ThreadStart(colorer.OpenColorWindow));
theThread.Start();
}
private void UpdateProgress(int linesComplete) {
// update progress bar here
}
public class ColoringThread
{
string text;
public delegate void HighlightEventHandler(int linesComplete);
public event HighlightEventHandler HighlightProgressChanged;
public ColoringThread(string initText) {
text = initText;
}
public void OpenColorWindow() {
Form2 form2 = new Form2(text);
form2.ShowDialog();
int linesColored = 0;
foreach (String line in text.Split(Environment.NewLine)) {
// colorize line here
// raise event
if (HighlightProgressChanged != null)
HighlightProgressChanged(++linesColored);
}
}
};
You can pass an object as argument to the Thread.Start and share your data between the current thread and the initiating thread.
Here is a good example:
How to share data between different threads In C# using AOP?
Or you can use BackgroundWorker which has ReportProgress
What you need is System.Windows.Threading.Dispatcher's BeginInvoke method. You can't directly modify a WPF object from your background thread, however you can dispatch a delegate to do that.
In your derived Window class object you have the Property Dispatcher, so you use it as follows:
Dispatcher.BeginInvoke(
DispatcherPriority.Normal,
(status) => { StatusTextBox.Text = status },
thestatus
);
I'm sorry that I can't test that currently and I don't have the project here, where I did that. But I'm sure it will work, good luck ;)
Update: Oops, you're using Form's... I've written about WPF, sorry.
I made a program that loads a bunch of computer information. In the Form_Load event I have it initialize 3 (that number will grow) panels of information. One that has a bunch of unit information seems to make the program load rather slowly. I've tried to speed it up a bunch by switching from WMI to using Native calls, which helped a bunch. Soon though I'm going to have network information posted as well. I used to load that panel but i disabled it for a little bit till I work out the bugs in my other panels. So while learning how I can use a seperate thread to update my battery information I figured that I might be able to create seperate threads in my unit information panel so that it might could load faster. I dont know that any of my information would cause concurrent issues, but i can work on that.
I want to start small so what if i change this
private void Form1_Load(object sender, EventArgs e)
{
unitInformationPanel1.PopulateUnitInformation();
batteryInformationPanel1.InitializeBatteries();
magStripeReaderPanel1.SetupPointOfSale();
}
to this
private void Form1_Load(object sender, EventArgs e)
{
Thread infoThread = new Thread(new ThreadStart(unitInformationPanel1.PopulateUnitInformation));
infoThread.Start();
batteryInformationPanel1.InitializeBatteries();
magStripeReaderPanel1.SetupPointOfSale();
}
would the info thread be terminated when populate unit info is done? or would it be better to move that thread creation into PopulateUnitInformation? here is what it looks like.
public void PopulateUnitInformation()
{
unitModelLabel.Text = Properties.Settings.Default.UnitModelString;
serialNumberLabel.Text = Properties.Settings.Default.UnitSerialString;
biosVersionLabel.Text = UnitBios.GetBiosNumber();
osLabel.Text = OS.getOSString();
cpuLabel.Text = UnitCpu.GetCpuInfo();
var hdd = HddInfo.GetHddInfo();
diskNameLabel.Text = hdd.Name;
diskCapacityLabel.Text = hdd.Capacity;
diskFirmwareLabel.Text = hdd.Firmware;
memoryLabel.Text = MemoryInformation.GetTotalMemory();
NetworkPresenceInformation.GetAdapatersPresent();
biometricLabel.Text = BiometricInformation.IsPresent ? "Present" : "Not Present";
var networkAdaptersPresense = NetworkPresenceInformation.GetAdapatersPresent();
bluetoothLabel.Text = networkAdaptersPresense[0] ? "Present" : "Not Present";
wifiLabel.Text = networkAdaptersPresense[1] ? "Present" : "Not Present";
cellularLabel.Text = networkAdaptersPresense[2] ? "Present" : "Not Present";
}
--
wow i just ran it with the infothread and it still took some time to load (might be the 12 panels i created in the main thread. but it loaded the 12 frames and the unit information panel populated its information after everything loaded. That was cool, but is it safe? is it somewhat easy to make 12 threads for my panels? or is that dumb?
EDIT
this is what i did for stopwatch.
Stopwatch programTimer;
public Form1()
{
programTimer = Stopwatch.StartNew();
InitializeComponent();
SetupDebugWindow();
TerminateKeymon();
UnitModel.SetModel();
UnitSerialNumber.SetSerialNumber();
}
private void Form1_Shown(object sender, EventArgs e)
{
audioBrightnessPanel1.UpdateBrightnessTrackbar();
applicationLauncherPanel1.LoadApplications();
programTimer.Stop();
Console.WriteLine("Load Time: {0}",programTimer.ElapsedMilliseconds);
timer1.Start();
}
Will this be accurate?
EDIT 2 6/18/2012
Well I took the advice of using backgroundworker. Please let me know if i did this right.
private void Form1_Load(object sender, EventArgs e)
{
backgroundWorker1.RunWorkerAsync();
}
void BackgroundWorker1DoWork(object sender, System.ComponentModel.DoWorkEventArgs e)
{
unitInformationPanel1.PopulateUnitInformation();
batteryInformationPanel1.InitializeBatteries();
magStripeReaderPanel1.SetupPointOfSale();
}
You've asked a very broad question, but I'm going to give some general advice. If you want more specific information, you should consider deleting this question and posting more specific individual questions.
First and foremost, you should very strongly consider using something like the System.Threading.Task class for your multithreaded operations. There is a ton of information online about how to get started with it and how you can use Tasks to manage asynchronous operations. The short story is that if you're spinning up your own thread (as you're doing above), you almost certainly should be using something else to do that for you.
Adding multithreading to your code will not, in the strictest sense of the word, make it any "faster"; they will always take the same amount of total processor time. What it can and will do is two things: free up the UI thread to be responsive and allow you to split that "total processor time" across multiple cores or processors, should those be available to the system. So, if you have operation X that takes 10 seconds to complete, then just shifting operation X to another thread will not make it complete any faster than 10 seconds.
No, what you are doing above is not safe. I'm assuming that somewhere you've turned off checking for cross-thread communication errors in your app? Otherwise, that code should throw an exception, assuming this is a WinForms or WPF application. This is one reason to use Tasks, as you can easily separate the part of your process that actually takes a long time (or isn't UI related), then add a task continuation that uses the results and populates the UI elements within a properly synchronized context.
So my final approach this was as follows. I felt that my Main Form was doing more than it should. Sticking with the single responsibility principle I decided that MainForm should only be responsible for one thing, showing and displaying all 12 panels (now down to 11, i turned one into a menu item). So moved all the multithreading out of mainform and into program.cs. I found that this was even a little more difficult. What I did find though was a simple solution that allows me to not even worry about multithreading at all. It was the Idle event. Here is what i chose to do.
[STAThread]
static void Main()
{
DateTime current = DateTime.Now;
DateTime today = new DateTime(2012,7,19);
TimeSpan span = current.Subtract(today);
if (span.Days<0)
{
MessageBox.Show("Please adjust Time then restart Aspects","Adjust Time");
Process.Start("timedate.cpl").WaitForExit();
}
else
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Idle += new EventHandler(Application_Idle);
mainForm = new MainForm();
mainForm.Closing += new CancelEventHandler(mainForm_Closing);
#if !DEBUG
TerminateKeymon();
StartSerial();
SetupDefaultValues();
EmbeddedMessageBox(0);
#endif
Application.Run(mainForm);
}
}
static void Application_Idle(object sender, EventArgs e)
{
Application.Idle -= Application_Idle;
mainForm.toolStripProgressBar1.Increment(1);
UnitInformation.SetupUnitInformation();
mainForm.toolStripProgressBar1.Increment(1);
Aspects.Unit.HddInfo.GetHddInfo();
mainForm.toolStripProgressBar1.Increment(1);
for (int i = 0; i < mainForm.Controls.Count; i++)
{
if (mainForm.Controls[i] is AbstractSuperPanel)
{
try
{
var startMe = mainForm.Controls[i] as AbstractSuperPanel;
startMe.StartWorking();
mainForm.toolStripProgressBar1.Increment(1);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message + mainForm.Controls[i].ToString());
}
}
}
mainForm.toolStripProgressBar1.Value = 0;
}
to sum up what that does is is I add a idle listener event. Once the thead goes idle (basically meaning that Mainform is finished drawing and making all 12 panels and is showing on my desktop) I then kill the idle event listener and tell all my panels and classes to start working one at a time, updating my progress bar as I go. It works great. The load time is still the same as it was before, but there is window visibile after only a few seconds. Maybe not the best use of resources, but i think the solution is simple and straight forward.
I had a question somewhat related to this for Mobile app development a few months back (see How to write a Trigger?), and Marc "the man" Gravell posted back with a simple class that I modified to return data to my main application whenever the thread was complete.
The actual class I put into use has loads of pointless data (for you), so I'm going to paste in a revised version of Mr. Gravell's code using techniques which I used to make them work:
First, I had to create my own EventArgs class:
public class SuperEventArgs : EventArgs {
private object data;
public SuperEventArgs(object data) : base() {
this.data = data;
}
public object Data { get { return data; } }
}
Using that, here is a class I created to pass my data back to the main thread:
public delegate event DataChangedHandler(object sender, SuperEventArgs e);
public class Simple1 {
private object parameter1, parameter2;
private Control parent;
#if PocketPC
public delegate void MethodInvoker(); // include this if it is not defined
#endif
public Simple1(Control frmControl, object param1, object param2) {
parent = frmControl;
parameter1 = param1;
parameter2 = param2;
}
public event DataChangedHandler DataChanged;
public void Start() {
object myData = new object(); // whatever this is. DataTable?
try {
// long routine code goes here
} finally {
if (DataChanged != null) {
SuperEventArgs e = new SuperEventArgs(myData);
MethodInvoker methInvoker = delegate {
DataChanged(this, e);
};
try {
parent.BeginInvoke(methInvoker);
} catch (Exception err) {
Log(err); // something you'd write
}
}
}
}
}
Back in the actual main thread of execution, you'd do something like this:
public partial class Form1 : Form {
private Simple1 simple;
public Form1() {
object query = new object(); // something you want to pass in
simple = new Simple1(this, query, DateTime.Now);
simple.DataChanged += new DataChangedHandler(simple1_DataChanged);
Thread thread = new Thread(simpleStart);
thread.Start();
}
private void simpleStart() {
if (simple != null) {
simple.Start();
}
}
private void simple1_DataChanged(object sender, SuperEventArgs e) {
MyFancyData fancy = e.Data as MyFancyData;
if (fancy != null) {
// populate your form with the data you received.
}
}
}
I know it looks long, but it works really well!
This is not anything I have actually tested, of course, because there isn't any data. If you get to working with it and you experience any issues, let me know and I'll happily help you work through them.
~JoeP