I am working on a project which includes two main processing, I have tried using timer_tick to handle the processing which makes the application really slow. The application needs to be running at all times, I wanted the timer aspect of the timer_tick to trigger the methods every X seconds but with the multiple threading, as this makes it a lot faster.
Can anyone help?
The current structure of the application is below:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
setting_info();
}
public void setting_info()
{
// takes data from config file for API connections
}
private void swis()
{
// connects to API and fetches data
// need to be continuously running - Ideally Interval(1200)
}
private void st_processing()
{
// processes the data that was fetched in swis() slows down the program without multiple threading
// need to be continuously running - Ideally Interval(800)
}
private void alert_in_timer_Tick(object sender, EventArgs e)
{
while (alert_in == true)
{
swis();
break;
}
}
private void st_processing_timer_Tick(object sender, EventArgs e)
{
while (st_processing == true && alert_data_database_has_data == true)
{
st_processing();
//check_error_count();
alert_data_database_has_data = false;
break;
}
}
}
It's not clear what you want to know. There are many good threading tutorials for C# as discussed here for example this one
Starting a thread is easy
Thread worker = new Thread(new ThreadStart(swis));
//...
worker.join();
You will need to be careful if you need to update the GUI as a result of what the thread does. Previously discussed here.
At the moment you break from the alert_in_timer_Tick once it's called the swis function.
while (alert_in == true)
{
swis();
break;// why is this here?
}
Related
I am working on a small app that would track certain GPU parameters. I am currently using 5 background workers for 5 different parameters that are being tracked, the operations run until my app is closed. I know this is probably not a good way to do it. What would be a good way to monitor these parameters in the background without having to create a worker for each parameter?
Edit: Reverted back to the original question that I asked now that the question was reopened.
Test file that monitors the temperature only.
using NvAPIWrapper.GPU;
using System.ComponentModel;
using System.Threading;
using System.Windows.Forms;
namespace TestForm
{
public partial class Form1 : Form
{
private PhysicalGPU[] gpus = PhysicalGPU.GetPhysicalGPUs();
public Form1()
{
InitializeComponent();
GPUTemperature();
}
private void GPUTemperature()
{
backgroundWorker1.RunWorkerAsync();
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
while (!backgroundWorker1.CancellationPending)
{
foreach (var gpu in gpus)
{
foreach (var sensor in gpu.ThermalInformation.ThermalSensors)
{
backgroundWorker1.ReportProgress(sensor.CurrentTemperature);
Thread.Sleep(500);
}
}
}
}
private void backgroundWorker1_ProgressChanged(object sender,
ProgressChangedEventArgs e)
{
temperature.Text = e.ProgressPercentage.ToString();
}
}
}
I was able to solve the issue after getting some help in the comments. Here is my final working code.
using NVIDIAGPU.GPUClock;
using NVIDIAGPU.GPUFan;
using NVIDIAGPU.Memory;
using NVIDIAGPU.Thermals;
using NVIDIAGPU.Usage;
using System.ComponentModel;
using System.Threading;
using System.Windows.Forms;
namespace SysMonitor
{
public partial class Form1 : Form
{
private int[] sensorValues = new int[5];
public Form1()
{
InitializeComponent();
StartWorkers();
}
/// <summary>
/// Store sensor parameters.
/// </summary>
public int[] SensorValues { get => sensorValues; set => sensorValues = value; }
private void StartWorkers()
{
thermalsWorker.RunWorkerAsync();
}
#region ThermalWorker
private void thermalsWorker_DoWork(object sender, DoWorkEventArgs e)
{
while (!thermalsWorker.CancellationPending)
{
// Assign array values.
SensorValues[0] = GPUThermals.CurrentTemeperature;
SensorValues[1] = GPUUsage.CurrentUsage;
SensorValues[2] = (int)GPUClock.CurrentGPUClock;
SensorValues[3] = (int)GPUMemory.CurrentMemoryClock;
SensorValues[4] = GPUFan.CurrentFanRPM;
// Pass the SensorValues array to the userstate parameter.
thermalsWorker.ReportProgress(0, SensorValues);
Thread.Sleep(500);
}
}
private void backgroundWorker1_ProgressChanged(object sender,
ProgressChangedEventArgs e)
{
// Cast user state to array of int and assign values.
int[] result = (int[])e.UserState;
gpuTemperatureValue.Text = result[0].ToString() + " °C";
gpuUsageValue.Text = result[1].ToString() + " %";
gpuClockValue.Text = result[2].ToString() + " MHz";
gpuMemoryValue.Text = result[3].ToString() + " MHz";
gpuFanRPMValue.Text = result[4].ToString() + " RPM";
}
#endregion ThermalWorker
}
}
I have a problem with all the existing answers as they all fail on basic separation of concerns.
However, to fix this problem, we need to reframe the question. In this case, we do not want to "run an operation", but instead want to "observer/subscribe" to the GPU state.
I prefer observe over sub/pub in this case, as work is done, and you really want to know if there is someone listening to your tree falling in the forest.
Therefore, here is the code for an RX.Net implementation.
public class GpuMonitor
{
private IObservable<GpuState> _gpuStateObservable;
public IObservable<GpuState> GpuStateObservable => _gpuStateObservable;
public GpuMonitor()
{
_gpuStateObservable = Observable.Create<GpuState>(async (observer, cancellationToken) =>
{
while(!cancellationToken.IsCancellationRequested)
{
await Task.Delay(1000, cancellationToken);
var result = ....;
observer.OnNext(result);
}
})
.SubscribeOn(TaskPoolScheduler.Default)
.Publish()
.RefCount();
}
}
Then to consume.
public class Form1
{
private IDisposable _gpuMonitoringSubscription;
public Form1(GpuMonitor gpuMon)
{
InitializeComponent();
_gpuMonitoringSubscription = gpuMon.GpuStateObservable
.ObserveOn(SynchronizationContext.Current)
.Susbscribe(state => {
gpuUsageValue.Text = $"{state.Usage} %";
//etc
});
}
protected override void Dispose(bool disposing)
{
_gpuMonitoringSubscription.Dispose();
base.Dispose(disposing);
}
}
The advantage here, is that you can reuse this component in multiple places, with different threads, etc.
My suggestion is to scrap the technologically obsolete BackgroundWorker, and use instead an asynchronous loop. The reading of the sensor values can be offloaded to a ThreadPool thread by using the Task.Run method, and the idle period between each iteration can be imposed by awaiting a Task.Delay task:
public Form1()
{
InitializeComponent();
StartMonitoringSensors();
}
async void StartMonitoringSensors()
{
while (true)
{
var delayTask = Task.Delay(500);
var (temperature, usage, gpuClock, memory, fan) = await Task.Run(() =>
{
return
(
GPUThermals.CurrentTemperature,
GPUUsage.CurrentUsage,
GPUClock.CurrentGPUClock,
GPUMemory.CurrentMemoryClock,
GPUFan.CurrentFanRPM
);
});
gpuTemperatureValue.Text = $"{temperature} °C";
gpuUsageValue.Text = $"{usage} %";
gpuClockValue.Text = $"{gpuClock} MHz";
gpuMemoryValue.Text = $"{memory} MHz";
gpuFanRPMValue.Text = $"{fan} RPM";
await delayTask;
}
}
What you get with this approach:
A ThreadPool thread is not blocked during the idle period between reading the sensors. This fact would be impactful if your application was more complex, and was making heavy use of the ThreadPool. But for a simple application like this, that does nothing else than displaying sensor values, this benefit is mostly academic. The ThreadPool thread will have nothing else to do during the idle period, and it will be idled anyway. In any case, it is a good habit to avoid blocking threads needlessly, whenever you can.
You get strongly typed sensor values passed to the UI thread. No need to cast from the object type, and no need to convert all the values to ints.
Any exceptions thrown by reading the GPUThermals, GPUUsage etc properties, will be rethrown on the UI thread. Your application will not just stop working, without giving any indication that something wrong happened. That's the reason for choosing async void in the signature of the StartMonitoringSensors method. Async void should be avoided in general, but this is an exceptional case where async void is preferable to async Task.
You get consistent updates of the sensor values every 500 msec, because the time needed to read the sensor values is not added to the idle period. This happens because the Task.Delay(500) task is created before reading the values, and it is awaited afterwards.
I'm using a Background worker to read values in and to pass values to Worker_ProgressChanged, to update UI.
In Worker_DoWork:
while (agi.DvmReadyToRead) // wait for digipot to be adjusted before reading in worker
{
Thread.Sleep(20);
Application.DoEvents();
//logS.Debug("Waiting for ready to read in worker");
}
Thread.Sleep(40); // Give digipot chance to make the change
agi.SendSoftwareTriggerOne();
Thread.Sleep(7); // Duration for above command to execute
A = agi.ReadOne();
Thread.Sleep(1);
agi.InitOne();
Thread.Sleep(1);
sAndH3 = A[0];
worker.ReportProgress(0, new System.Tuple<double>(sAndH3));
agi.DvmReadyToRead = true;
In Worker_ProgressChanged:
while (!agi.DvmReadyToRead)
{
//logS.Debug("waiting for ready to read in progress");
Thread.Sleep(0);
Thread.Sleep(0);
Thread.Sleep(0);
Thread.Sleep(0);
Thread.Sleep(0);
Application.DoEvents(); // Exception thown here
Thread.Sleep(1); // wait for DVM reading
}
agi.DvmReadyToRead = false;
// Then goes on to adjust output voltage up or down
This is working fine the first time round using
Application.DoEvents();
however after first run, I get a stackoverflow at this point. After reading many posts on here DoEvents is not the best way of doing what I am trying to achieve.
So what I would like is a way to pass a Boolean back to DoWork, or another way to allow worker to be able to read the agi.DvmReadyToRead Boolean.
Thanks!
If I understand your question, you are describing a very common pattern in Test and Measurement where you have an instrument that takes some time after triggering it before it gets a reading. But you want to know when the reading happens so that you can take some action (like update a ProgressBar or TextBox perhaps) and you want be able to cancel the worker loop.
When I need to do this myself, I like to use the System.Threading.Tasks to simplify things. I'll post a complete pattern here in the hope that you can find something of use to solve the issue you are having.
To be clear, I am trying to answer your question of "a way to pass a Boolean back to DoWork..." by saying that one way to do this is to fire an Event from Worker_DoWork that can contain Boolean (like you asked) or double (in my example) or any other information you choose.
Good luck!
using System;
using System.ComponentModel;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace StackOverflow02
{
public partial class DVMLoopRunner : Form
{
public DVMLoopRunner()
{
InitializeComponent();
DVMReadingAvailable += Form1_DVMReadingAvailable;
ContinueOrCancel += Form1_ContinueOrCancel;
}
// See if User has turned off the Run button then cancel worker
private void Form1_ContinueOrCancel(Object sender, CancelEventArgs e)
{
e.Cancel = !checkBoxRunMeterLoop.Checked;
}
// The DVM, after being triggered + some delay, has come up with a new reading.
private void Form1_DVMReadingAvailable(Object sender, DVMReadingAvailableEventArgs e)
{
// To update GUI from worker thread requires Invoke to prevent Cross-Thread Exception
Invoke((MethodInvoker)delegate
{
textBox1.Text = e.Reading.ToString("F4");
});
}
// Make our events so that we can be notified of things that occur
public event CancelEventHandler ContinueOrCancel;
public event DVMReadingAvailableEventHandler DVMReadingAvailable;
// This is how we will provide info to the GUI about the new reading
public delegate void DVMReadingAvailableEventHandler(Object sender, DVMReadingAvailableEventArgs e);
public class DVMReadingAvailableEventArgs : EventArgs
{
public readonly double Reading;
public DVMReadingAvailableEventArgs(double reading)
{
Reading = reading;
}
}
// When the User checks the box, Run the worker loop
private void checkBoxRunMeterLoop_CheckedChanged(Object sender, EventArgs e)
{
if(checkBoxRunMeterLoop.Checked)
{
Task.Run(() => ReadDVMWorker());
}
}
// Worker Loop
private void ReadDVMWorker()
{
while(true)
{
CancelEventArgs e = new CancelEventArgs();
ContinueOrCancel?.Invoke(this, e);
if (e.Cancel) return; // If User has turned off the Run button then stop worker
ReadDVM(); // This worker thread will block on this. So trigger, wait, etc.
}
}
// DVM Takes some period of time after trigger
void ReadDVM()
{
Thread.Sleep(1000);
double newSimulatedReading = 4.5 + Random.NextDouble();
DVMReadingAvailable?.Invoke(this, new DVMReadingAvailableEventArgs(newSimulatedReading));
}
Random Random = new Random(); // Generate random readings for simulation
}
}
Why does my first attempt to change a button's text in this code not work, while the third attempt does work ?
My user will have to wait a few seconds while the serial port connects. After that, I want to alert him that he has already connected (a second attempt can cause problems).
I wanted to let him know that things are okay, so he won't think "duh" and click twice.
Fail. The text change never appears.
Okay, why does the third change in button text work, but the first one does not ? I don't know if the second one works or not.
/***********************************************************************
* Button: Connect Port *
***********************************************************************/
private void TheConnectPortButton_Click(object sender, EventArgs e)
{
string OldText = TheConnectPortButton.Text;
TheConnectPortButton.Text = "Busy, Please Wait"; /////// This never happens
ButtonBoss.ButtonHandler_ConnectPort();
TheConnectPortButton.Text = OldText; /////// Not sure if this happens
if (aUartSemaphoreThatTells.IfWeHaveConnectedToAPort == (int)aValueWhichIndicatesThat.YesWeHaveAGoodPortConnected)
{
TheConnectPortButton.Text = "Connected"; /////// This one does happen
}
}
the aUartSemaphoreThatTells.IfWeHaveConnectedToAPort is also used by the ButtonBoss routine to make sure he doesn't connect a second time, along with other button routines (e.g., make sure we are connected before we Tx/Rx or whatever).
I tried changing the code after the routine returns to look like this...
if (aUartSemaphoreThatTells.IfWeHaveConnectedToAPort == (int)aValueWhichIndicatesThat.YesWeHaveAGoodPortConnected)
{
TheConnectPortButton.Text = "Connected";
}
else
{
TheConnectPortButton.Text = OldText;
}
...and I still get the same result.
My guess (and that's all it is) is that threading is somehow involved in all this, and that the serial port routines trump the button text changing routines by some convolution that I don't follow properly at the moment.
Question: What do I need to do to get the text to change before the connection stuff hogs the system ?
(If that's what's happening)
Question 2: If I can't make this happen, I think I've read about "greying out" the buttons, or, I believe I saw somewhere that I can actually make a button go away right before the user's eyes so that he can't click it again. Links to example code would be welcome.
The problem is you're doing everything from one and the same event-handler consequently, so that the button has no time to get updated (redrawn). You could call Application.DoEvents(); method, but it's not a good idea at all, please, read Use of Application.DoEvents()
I think usually you're expected to push a time-consuming task into a separate thread, get progress report from it and update your GUI. There is a plenty of ways to create a "worker" thread and get some respond from it. For example, use a BackgroundWorker Class:
public partial class Form1 : Form
{
public Form1() { InitializeComponent(); }
private void button1_Click(object sender, EventArgs e)
{
BackgroundWorker w = new BackgroundWorker();
w.WorkerReportsProgress = true;
w.DoWork += new DoWorkEventHandler(w_DoWork);
w.ProgressChanged += new ProgressChangedEventHandler(w_ProgressChanged);
w.RunWorkerCompleted += new RunWorkerCompletedEventHandler(w_RunWorkerCompleted);
w.RunWorkerAsync();
button1.Text = "Started";
}
//may influence GUI, as this event handler is run on the GUI thread
void w_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
button1.Text = "Job is done";
}
//may influence GUI, as this event handler is run on the GUI thread
void w_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
button1.Text = e.ProgressPercentage.ToString();
}
//runs in the worker thread...should do the actual job
//may influence GUI through `ReportProgress`
//or through `Invoke` method
void w_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker worker = sender as BackgroundWorker;
for (int i = 1; i <= 10; i++)
{
Thread.Sleep(500);
worker.ReportProgress(10 * i);
}
}
}
Or you may use Task Class:
public partial class Form1 : Form
{
public Form1() { InitializeComponent(); }
private void button1_Click(object sender, EventArgs e)
{
new Task(DoTask).Start();
}
void DoTask()
{
for (int i = 1; i <= 10; i++)
{
Thread.Sleep(500);
//here you need to update GUI through `Invoke` method
//as the GUI may only be influenced from the the thread,
//where it's created
this.Invoke(new Action<int>((j) =>
{
button1.Text = j.ToString();
}), 10 * i);
}
}
}
I am using a background for my sms application.
I have created an application which needs to send messages continously.
When i use background.My application gets hanged i dont know why it is,
my code is
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
//mycode
}
private void backgroundWorker1_RunWorkerCompleted(
object sender, RunWorkerCompletedEventArgs e)
{
backgroundWorker1.CancelAsync();
backgroundWorker1.RunworkAsync();
}
later i have tried this
public void Thread()
{
Thread D = new Thread(BackgroundCode);
D.Start();
}
public void BackgroundCode()
{
for (int i5 = 1; i5 > 0; i5 += 1)
{
//mycode
}
}
this two works fine for 2 or 3 min but after that it gets hang.
Please can anyone please help me.
Thanks In Advance.
try this
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
while(true)
{
//your sms code
//call CancelAsync() here if you want to get out!!!!
}
}
private void backgroundWorker1_RunWorkerCompleted(
object sender, RunWorkerCompletedEventArgs e)
{
//donot include RunWorkerAsync() and CancelAsync() in this method!
}
This is a bit of a long post, but it seems to fit your problem quite well.
Basically, it shows how you can do this with a BackgroundWorker or the Task Parallel Library (TPL would be my suggestion)
For the TPL you create a ProgressReporter class that will be used in the class to pass messages back to the main thread, while continuing to run the task.
Also, your for loop will never end because it is saying to run while i5 is greater than 0. And, it starts at 1 and counts up, so it will never be less than or equal to zero. That will fix your hanging, but once you fix that, the progress reporter from the blog class should help with the rest.
trying to run a function without putting it in the Main() when the program is run.
how do I start the new created function?
trying to call RunMix() in the Main() but get an error because of the lable1
namespace mixer
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
int i = 0;
public void RunMix()
{
while (i == 0)
{
label1.Text = knob1.Angle.ToString();
Application.DoEvents();
}
}
private void Form1_Load(object sender, EventArgs e)
{
RunMix();
}
}
}
In a console application, the Main() method is the entry point into the application. You have to put you code to start the application in there.
If you only want to test the function you can use the NUNIT or the Microsofts Unit Testing Framework. Otherwise you have to call the function from the Main().
Alright my first answer was completely off the topic because of your mysterious question. Now that you have updated it I have better - not complete - understanding of what do you mean.
Looking at code I guess what you are trying to do is to change the value of label when knob1 control's angle changes. If knob1 is a control it should have a change event and you should change value of label1 inside knob1_change event handler. If knob1 doesn't have any event - highly unlikely - then you should use a timer instead. Loop is simply a bad idea in your situation. Timer should work like this
Timer timer = new Timer();
public void RunMix(object sender, EventArgs e)
{
label1.Text = knob1.Angle.ToString();
}
private void Form1_Load(object sender, EventArgs e)
{
timer.Interval = 100;
timer.Tick += new EventHandler(RunMix);
timer.Start();
}
Stop timer when form is closed or use activate/deactivate cycle depending upon your requirement.
You can't have another method besides Main as an entry point for the app.
For ex you can't start a program from a function like this:
public static MyMain(string[] args)
{
}
This is a java code for the same but i don't know the same in C#.
But i think it can be possible in C# too.
class staticEx {
static
{
System.out.println("Inside Static
Block");
System.exit(0);
} }
The above code is tested and got it from here while GOOGLEing.
There can be a possibility of similar thing in C# as well.