I have considerable programming knowledge but this is the first time I'm working on a multi-threaded application on C#, so I'm asking for help regarding my problem.
Firstly, the codes,
public frmCEX()
{
InitializeComponent();
refreshTicker();
}
private void btnRefresh_Click(object sender, EventArgs e)
{
refreshTicker();
}
private void refreshTicker()
{
ssStatus.Text = "Updating ticker..";
btnRefresh.Text = "Updating";
btnRefresh.Enabled = false;
ssUpdated.Text = "Last updated: -";
APIManager apim = new APIManager();
Ticker tk = apim.getTicker();
//blablabla, do some work
ssUpdated.Text = "Last updated: " + DateTime.Now.ToString();
ssStatus.Text = "";
btnRefresh.Text = "Refresh";
btnRefresh.Enabled = true;
}
private void cbxRefresh_CheckedChanged(object sender, EventArgs e)
{
if (cbxRefresh.Checked)
{
Thread thread1 = new Thread(() => BGRefreshThread(Convert.ToInt32(nupRefreshSecs.Value)));
thread1.Start();
}
else
{
// IF REFRESH CHECKBOX IS UNCHECKED, STOP THE THREAD THAT IS REFRESHING
}
}
private void BGRefreshThread(int delay)
{
refreshTicker();
System.Threading.Thread.Sleep(delay * 1000);
}
My main problem is in the cbxRefresh_CheckedChanged method, basically, how this works is that when the user check the "Auto refresh" checkbox in the main UI, the checkedchanged method will create a new thread BGRefreshThread that runs in the background and refresh the ticker, and once the checkbox is unchecked again, it will end the thread that refreshes the ticker.
However, I am having problem ending the thread once it is started, since once the checkedchanged method ends, the thread no longer exists in the context when the checkbox is unchecked the next time.
Can anybody advice how I can get this working? Really new to multi-threading programming.
**EDIT: I've found a solution for this problem, but right now, when the newly created thread tries to call "refreshTicker" which updates labels and buttons on the main UI (which is on the main thread), it gives me this error:
Cross-thread operation not valid: Control 'btnRefresh' accessed from a thread other than >the thread it was created on.
any advice on this?**
First of all, the thread variable should be an instance member so you have access to it at any time.
What I do to stop threads gracefully is: Before I create and start the thread I create a ManualResetEvent, which I use to signal the thread to quit:
private ManualResetEvent m_stopThread = new ManualResetEvent(false);
To stop the thread I set the event somewhere in my code and wait for the thread to end:
m_stopThread.Set();
m_myThread.Join();
The thread's code needs to account for stopping:
while (true)
{
// Stop the thread if the handle is set after 1 ms
if (m_stopThread.WaitOne(1, false))
break;
// Do some work
}
You need to save the thread in a variable that does not go out of scope.
private Thread thread1;
private void cbxRefresh_CheckedChanged(object sender, EventArgs e)
{
if (cbxRefresh.Checked)
{
thread1 = new Thread(() => BGRefreshThread(Convert.ToInt32(nupRefreshSecs.Value)));
thread1.Start();
}
else
{
thread1.Abort();
}
}
Please note that there are some checks missing and abort is not a nice way to end a thread.
Related
I've been trying to learn more about asynchronous tasks and threading but not making a ton of headway.
I'm trying to load an "Engine" type of thread that will run in the background upon launch and be able to access the UI Thread to update variables, without hanging the UI Thread.
In the below code, Engine is called, and a Ticker object is created which holds the current value of (Litecoin/USD) called Last, also holds several other values that would be useful. This code successfully assigns the current value to label1.text. I don't necessarily need code but what approach would I take to create a ticker object in the background every second and update the UI thread with each new Ticker objects values.
Is this a good case for a background worker?
private void Form1_Load(object sender, EventArgs e)
{
Engine();
}
private void Engine()
{
Ticker ltcusd = BtceApi.GetTicker(BtcePair.LtcUsd);
label1.Text = "LTC/USD:" + ltcusd.Last;
}
EDIT:
If I do the following, label1 throws an InvalidOperationException due to a Cross-thread operation attempt (label1 in the UI thread).
private void Form1_Load(object sender, EventArgs e)
{
var t = Task.Factory.StartNew(() => Engine());
t.Start();
}
private void Engine()
{
while (true)
{
Thread.Sleep(1000);
Ticker ltcusd = BtceApi.GetTicker(BtcePair.LtcUsd);
label1.Text = "LTC/USD: " + ltcusd.Last;
}
}
Using async/await, the simplest way of getting an "asynchronous" sort of API is to invoke a new task. It's not great, but it'll make things simpler. I would probably create a new class which basically wrapped all the BtceApi methods in tasks:
public class BtceApiAsync
{
public Task<Ticker> GetTickerAsync(BtcePair pair)
{
return Task.Run(() => BtceApi.GetTicker(pair));
}
// etc
}
Then you can use a timer which fires once per second, which will start off a new task and update the UI appropriately:
// Keep a field of type System.Windows.Forms.Timer
timer = new Timer();
timer.Interval = 1000;
timer.Tick += DisplayTicker;
timer.Start();
...
private async void DisplayTicker(object sender, EventArgs e)
{
Ticker ticker = await BtceApiAsync.GetTickerAsync(BtcePair.LtcUsd);
label1.Text = "LTC/USD: " + ltcusd.Last;
}
Note that this doesn't mean the screen will be updated once per second... there will be a new task started once per second, and as soon as each task completes, the UI will be updated.
The use of await here - from an async method started on the UI thread - means you don't need to worry about using the UI; the whole async method will execute on the UI thread, even though the fetch itself happens in a different thread.
You can try ContinueWith to update the Label at the end of the task. If you want to update it event before the task ends then raise an event which is registered by on the UI thread. The event can then update the label.
I suppose this is Windows Forms. You could do it "old school style" and set the label text on the UI thread, and you can do that by passing delegate to the BeginInvoke or Invoke method.
private void Engine()
{
while (true)
{
Thread.Sleep(1000);
Ticker ltcusd = BtceApi.GetTicker(BtcePair.LtcUsd);
UpdateText("LTC/USD: " + ltcusd.Last);
}
}
private void UpdateText(string text)
{
//Inspect if the method is executing on background thread
if (InvokeRequired)
{
//we are on background thread, use BeginInvoke to pass delegate to the UI thread
BeginInvoke(new Action(()=>UpdateText(text)));
}
else
{
//we are on UI thread, it's ok to change UI
label1.Text = text;
}
}
When a user clicks on Run, the application runs through a lot of code to generate a model and display it in a Chart. The Run takes about 1-2 minutes to run. I also have a Cancel button that gets enabled after the Run button is clicked. I am working with DotSpatial, so my buttons are on a plugin panel in a ribbon UI. The click event on the Run and Cancel start in the plugin, which calls the back-end class's code Run and Click.
When the user hits cancel after the run starts, there is a delay, but the cancel method is invokes and executes, but the run never stops and we eventually see the chart display. So, I'm thinking I need a separate thread for the Run. I'm fairly new to programming, and never worked with Threading. I've looked into it and added the below code, but my thread method isn't running. Here's my code:
The Run button is clicked:
This is at the top:
//check to see if RunModel thread needs to stop or continue
private volatile bool stopRun = false;
private Thread runThread;
Then this is the method that's called from the click event:
public void btnRun_testingThread(object sender, EventArgs e)
{
//create a new thread to run the RunModel
if (runThread == null)
{
//we don't want to stop this thread
stopRun = false;
runThread = new Thread(RunModel);
runThread.Start(); <--this isn't doing anything
}
So, I would think that when the code gets to the runThread.Start(), it would jump into my RunModel method and start running through the code. But it doesn't. Additionally, I'll want to cancel out of this thread (once I have it working correctly), so I have this, which gets called from the cancel click method:
private void StopRunThread()
{
if (runThread != null)
{
//we want to stop the thread
stopRun = true;
//gracefully pause until the thread exits
runThread.Join();
runThread = null;
}
}
Then the this is the RunModel() where I'm checking occasionally to see if the stopRun bool has changed.
public void RunModel()
{
...some code.....
//check to see if cancel was clicked
if (stopRun)
{
....clean up code....
return;
}
....some more code....
//check to see if cancel was clicked
if (stopRun)
{
....clean up code....
return;
}
}
And the cancel button click method:
public void btnCancel_Click(Object sender, EventArgs e)
{
stopRun = true;
StopRunThread();
//the model run has been canceled
....some code.....
}
Any help on getting the thread.start to actually run the Run method? Then do I need to constantly check the volatile bool in the run in order to clean everything up if it's being stopped? Thanks!
I think you'd be best looking at the BackgroundWorker - this essentially runs separately but can watch out for cancellation commands. Make sure you add 'WorkerSupportCancellation' when you initialise it:
BackgroundWorker backgroundWorker1 = new BackgroundWorker();
backgroundWorker1.DoWork += new DoWorkEventHandler(backgroundWorker1_DoWork); // This does the job ...
backgroundWorker1.WorkerSupportsCancellation = true; // This allows cancellation.
Then on click you can start your process:
public void btnRun_testingThread(object sender, EventArgs e)
{
backgroundWorker1.RunWorkerAsync();
}
Your cancel button can issue a cancellation request:
public void btnCancel_Click(Object sender, EventArgs e)
{
backgroundWorker1.CancelAsync();
}
Then your worker can monitor for this as it's doing it's work ...
void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
for (int i = 0; i < 100; i++)
{
if (backgroundWorker1.CancellationPending)
{
break;
}
else
{
// Do whatever you're doing.
}
}
e.Result = backgroundWorker1.CancellationPending ? null : orders;
}
You can enhance this further by adding progress bars etc., but that gets a bit more complicated so I won't go into it here.
Considering new info provided in commend I believe you just missed a start of the RunModel() method in debugger because of wrong assumption regarding thread.Start() method behaviour.
Please see a note from MSDN, Thread.Start Method
Once a thread is in the ThreadState.Running state, the operating
system can schedule it for execution. The thread begins executing
at the first line of the method represented by the ThreadStart or
ParameterizedThreadStart delegate supplied to the thread constructor.
Small demonstration that thread start takes some time bits, for me it starts in 38-40 milliseconds:
Stopwatch watch = new Stopwatch();
Thread thread = new Thread((ThreadStart)watch.Stop);
thread.Start();
watch.Start();
Thread.Sleep(5000);
double startedAfter = watch.ElapsedMilliseconds;
Since .NET Framework 4.0 consider using TPL Tasks rather than threads explicitly, some pros:
You can easily synchronize with UI thread by passing in a Task UI Thread synchronization context
You can easily stop a Taks using CancellationToken
EDIT #1: I have placed worker.RunWorkerAsync() within my timer loop and my application does not shut down anymore. Although nothing seems to happen now.
For performance reasons i need to replace DispatcherTimers with a other timer that runs in a different thread. There are to much delays / freezes so DispatcherTimer is no longer a option.
I am having problems to actually update my GUI thread, my application always seems to shut down without any warnings / errors.
I have mainly been trying to experiment with BackGroundWorker in attempt to solve my problem. Everything results in a shut down of my application when i launch it.
Some code examples would be greatly apperciated.
Old code dispatcher code:
public void InitializeDispatcherTimerWeging()
{
timerWegingen = new DispatcherTimer();
timerWegingen.Tick += new EventHandler(timerWegingen_Tick);
timerWegingen.Interval = new TimeSpan(0, 0, Convert.ToInt16(minKorteStilstand));
timerWegingen.Start();
}
private void timerWegingen_Tick(object sender, EventArgs e)
{
DisplayWegingInfo();
CaculateTimeBetweenWegingen();
}
Every 5 seconds the DisplayWegingInfo() and Calculate method should be called upon.
The GUI updates happen in the Calculate method. There a button gets created dynamically and added to a observerableCollection.
Button creation (short version):
public void CreateRegistrationButton()
{
InitializeDispatcherTimerStilstand();
RegistrationButton btn = new RegistrationButton(GlobalObservableCol.regBtns.Count.ToString());
btn.RegistrationCount = GlobalObservableCol.regBtnCount;
btn.Title = "btnRegistration" + GlobalObservableCol.regBtnCount;
btn.BeginStilstand = btn.Time;
GlobalObservableCol.regBtns.Add(btn);
GlobalObservableCol.regBtnCount++;
btn.DuurStilstand = String.Format("{0:D2}:{1:D2}:{2:D2}", 0, 0, 0);
}
New code using threading timer that runs in a different thread then the GUI
public void InitializeDispatcherTimerWeging()
{
worker = new BackgroundWorker();
worker.DoWork += new DoWorkEventHandler(Worker_DoWork);
worker.RunWorkerAsync();
}
void Worker_DoWork(object sender, DoWorkEventArgs e)
{
TimerCallback callback = MyTimerCallBack;
timerWegingen = new Timer(callback);
timerWegingen.Change(0, 5000);
}
private void MyTimerCallBack(object state)
{
DisplayWegingInfo();
CaculateTimeBetweenWegingen();
}
I timer runs in a separate thread then the GUI thread (that dispatcherTimer uses). But i cannot seem to be able to send this update to the UI thread itself so the updates get actually implemented in the UI.
The button gets refilled with new values every 1 sec trough a other timer. "DuurStilstand" is a dependency property
private void FillDuurStilstandRegistrationBtn()
{
TimeSpan tsSec = TimeSpan.FromSeconds(stopWatch.Elapsed.Seconds);
TimeSpan tsMin = TimeSpan.FromMinutes(stopWatch.Elapsed.Minutes);
TimeSpan tsHour = TimeSpan.FromMinutes(stopWatch.Elapsed.Hours);
if (GlobalObservableCol.regBtns.Count >= 1
&& GlobalObservableCol.regBtns[GlobalObservableCol.regBtns.Count - 1].StopWatchActive == true)
{
GlobalObservableCol.regBtns[GlobalObservableCol.regBtns.Count - 1].DuurStilstand =
String.Format("{0:D2}:{1:D2}:{2:D2}", tsHour.Hours, tsMin.Minutes, tsSec.Seconds);
}
}
Would i need to use the invoke from Dispatcher in the above method? If so how exactly?
Not sure how to call the ui thread after initializing the doWork method of the BackGroundWorker, my application keeps shutting down after right after start up.
I have tried using Dispatcher.BeginInvoke in several methods but all failed so far. At the moment i have no clue how to implement it.
All the above code is written in a separate c# class.
Best Regards,
Jackz
When I ran my sample of your code, the DisplayWegingInfo() was throwing an exception trying to access UI components. We need to call Invoke() from the Timer thread to update the UI. See DisplayWegingInfo() below. Note: this assumes that CaculateTimeBetweenWegingen() does not interact with the UI.
void Worker_DoWork(object sender, DoWorkEventArgs e)
{
TimerCallback callback = MyTimerCallBack;
timerWegingen = new System.Threading.Timer(callback);
timerWegingen.Change(0, 3000);
}
private void MyTimerCallBack(object state)
{
DisplayWegingInfo();
CaculateTimeBetweenWegingen();
}
private void DisplayWegingInfo()
{
if (this.InvokeRequired)
{
this.Invoke(new Action(DisplayWegingInfo));
return;
}
// at this point, we are on the UI thread, and can update the GUI elements
this.label1.Text = DateTime.Now.ToString();
}
private void CaculateTimeBetweenWegingen()
{
Thread.Sleep(1000);
}
My application fetches data from a live feed, processes it and displays the results. This data is updated every 5 seconds. In the Load event of Main form I've created a thread to show the splash screen which is shown until the first data cycle is run .
The data fetching and processing thread (RecieverThread) calls RecieveFeed. The isue I'm facing is that form2 which displays data fetched in RecieveFeed is shown before the first cycle is run completely. How do I ensure that form2 is loaded only after the first cycle has completed fetching data.
Code in the Main form:
private void frmMain_Load(object sender, EventArgs e)
{
Hide();
// Create a new thread from which to start the splash screen form
Thread splashThread = new Thread(new ThreadStart(StartSplash));
splashThread.Start();
//Thread to call the live feed engine. This thread will run for the duration of application
ReceiverThread = new System.Threading.Thread(new System.Threading.ThreadStart(ReceiveFeed));
ReceiverThread.Start();
frmSecondForm form2 = new frmSecondForm();
form2.MdiParent = this;
form2.WindowState = FormWindowState.Maximized;
Show();
form2.Show();
}
public frmRaceRace()
{
InitializeComponent();
this.splash = new SplashScreen();
}
private void StartSplash()
{
splash.Show();
while (!done)
{
Application.DoEvents();
}
splash.Close();
this.splash.Dispose();
}
private void ReceiveFeed()
{
while (!StopReceivingData)
{
foreach (...)
{
//Fetches data from live engine
DLLImportClass.GetData1();
//Manipulates and stores the data fetched in datatables
ThreadPool.QueueUserWorkItem(new WaitCallback(delegate { StoreData(); }))
rowsProcessed++;
if (!done)
{
this.splash.UpdateProgress(100 * rowsProcessed / totalRows);
}
}
done = true;
Thread.Sleep(5000);
}
}
I think what you need to use here is System.Threading.AutoResetEvent. Basically, add a member of this to your form class:
private AutoResetEvent waitEvent_ = new AutoResetEvent(false); // create unininitialized
After showing your splash, you want to wait for this event to be signalled:
private void StartSplash()
{
splash.Show();
// this will time out after 10 seconds. Use WaitOne() to wait indefinitely.
if(waitEvent_.WaitOne(10000))
{
// WaitOne() returns true if the event was signalled.
}
} // eo StartSplash
Finally, in your processing function, when you're done, simply call:
waitEvent_.Set();
Looks like you've got some race conditions in your code.
When doing threading with WinForms and most (if not all) UI frameworks, you can ONLY access the UI objects (forms and controls) from a single thread.
All other threads can only access that thread using .InvokeRequired() and .BeginInvoke(). These calls can be used to run a delegate in the UI thread. See:
{REDACTED: StackOverflow will only allow me to post 1 hyperlink. Google these}
There is a builtin shortcut for this in the BackgroundWorker class.
http://msdn.microsoft.com/en-us/library/system.componentmodel.backgroundworker.aspx
Simply do this (Psuedocode):
public void StartSplash()
{
Splash.Show();
BackgroundWorker bgw = new BackgroundWorker();
// set up bgw Delegates
bgw.RunWorkerAsync();
}
public void bgw_DoWork( ... etc
{
// do stuff in background thread
// you cannot touch the UI from here
}
public void bgw_RunWorkerCompleted( ... etc
{
Splash.close();
// read data from background thread
this.show(); // and other stuff
}
Now, you're guaranteed not to close the SplashScreen and not to start the main window before your data is delivered.
Other considerations: you'll probably need to use locks to secure the data you might access in the background thread. You should never access data in more than 1 thread without locking it.
Change your frmMain_Load to this:
private void frmMain_Load(object sender, EventArgs e)
{
Hide();
//Thread to call the live feed engine. This thread will run for the duration of application
ReceiverThread = new System.Threading.Thread(new System.Threading.ThreadStart(ReceiveFeed));
ReceiverThread.Start();
frmSecondForm form2 = new frmSecondForm();
form2.MdiParent = this;
form2.WindowState = FormWindowState.Maximized;
StartSplash();
Show();
form2.Show();
}
I have question about progressbar show value.
I have this main thread
private void button1_Click(object sender, EventArgs e)
{
progress prog = new progress();
progress.progressEvent += new progress.progressEventHandler(progressEvent);
for(int i=0;i<100;i++)
{
Thread.Sleep(100);
prog.incA();
}
}
void progressEvent(object sender)
{
if (progressBar1.InvokeRequired)
{
//Tady mi to caka az kym nedobehne cyklus for a pak zacne tohleto fungovat
progressBar1.Invoke(new ChangeProgressBarValue(ProgressStep));
}
else
{
ProgressStep();
}
}
public void ProgressStep()
{
progressBar1.PerformStep();
}
public class progress
{
private ThreadStart ts;
private Thread th;
private bool status = true;
public delegate void progressEventHandler(object sender);
public static event progressEventHandler progressEvent;
private int b,a = 0;
public progress()
{
ts=new ThreadStart(go);
th = new Thread(ts);
th.IsBackground = true;
th.Start();
}
public void incA()
{
a++;
if(a==100)
status = false;
}
private void go()
{
while (status)
{
if (a != b)
{
b = a;
if (progressEvent != null)
progressEvent(this);
}
}
th.Abort();
}
}
and my problem is IF start main thread and call IncA this method call event and in event is progressbar invoke
and this invoke waiting to end main thread FOR
why waiting?
thx
Your loop in the main thread is preventing "paint" events from happening. Since you are calling your progress bar's function from withing that thread, you will never see the updates.
You need to move the code to do the incrementing to another thread entirely.
Here is a sample of what you want to do using a Button, a BackgroundWorker, and a ProgressBar:
private void button1_Click(object sender, EventArgs e)
{
backgroundWorker1.RunWorkerAsync();
}
private void backgroundWorker1_DoWork(object sender, System.ComponentModel.DoWorkEventArgs e)
{
for (int i = 1; i <= 100; i++)
{
backgroundWorker1.ReportProgress(i);
Thread.Sleep(100);
}
}
private void backgroundWorker1_ProgressChanged(object sender, System.ComponentModel.ProgressChangedEventArgs e)
{
this.progressBar1.Value = e.ProgressPercentage;
}
Hope this helps!
The progress bar control is a UI object, and is created on the UI thread. When you call Invoke or BeginInvoke to update it, you are asking the UI thread to do the updating.
However, the UI thread is busy - in your button CLick event handler, you have a loop which Sleep()s the thread and calls prog.IncA in a loop. So it never exits back to the main UI loop (which is what dispatches windows messages and updates the UI). Your progress bar is being updated internally, but it never gets a chance to redraw because the UI thread is "busy".
The "processing" code (that is looping and calling prog.IncA()) should not be running on the UI thread at all - you need to start it off in a separate thread and then exit your Click handler so that the UI can continue to update.
Note that this has a side effect - if your UI thread is running, then the user will be able to continue interacting with your program, and so they can click again on the button and kick off another background thread - so you have to be very careful to make sure that the user can't do anything "dangerous" in the UI while you are busy processing.
I suggest you look at some introduction-to-threading tutorials to get an idea of how to use BackgroundWorker or another mechanism for running code in a separate thread. Once you understand that, you can add a progress bar. (And note that although a progress bar sounds like the simplest thing to do, it is actually rather a difficult thing to do due to the need for the UI thread to continue running but not let the user do anything dangerous during your processing)