I am working on Silverlight project based on MVVM architechture.
on click of a button a c# linq query gets executed which takes some time to execute (about one and half minute) due to which my UI hangs for this much time untill the response is received.
I'm having a custom progress bar which needs to be shown in between.
I tried to execute this linq statement on a background thread but no success.
Current Code:
private void _selectRecords()
{
//linq-query
}
I tried below steps,
private void _selectRecords()
{
System.Threading.Thread worker = new System.Threading.Thread(GetData);
worker.Start();
}
private void GetData()
{
//linq-query
}
EDIT :
in above case while execution getting Exception Invalid cross-thread access.
and
private void _selectRecords()
{
System.Threading.Thread worker = new System.Threading.Thread(GetData);
worker.Start();
}
private void GetData()
{
ApplicationConstants.AppConstants.WaitCursor = true; //MY PROGRESS BAR
Deployment.current.Dispatcher.BeginInvoke(()=>{
//linq-query
});
}
how can i run this linq statement on a background thread?
How about using BackgroundWorker
Declare a backgroundworker variable like this in your view Class
BackgroundWorker _worker;
in the constructor of your view initiate a Backgroundworker
like this
_worker = new BackgroundWorker();
_worker.DoWork += new DoWorkEventHandler(_worker_DoWork);
_worker.ProgressChanged += new ProgressChangedEventHandler(_worker_ProgressChanged);
void _worker_DoWork(object sender, DoWorkEventArgs e)
{
//call your getdata() method here
GetData();
}
In button click you can start this backgroundworker like this
m_oWorker.RunWorkerAsync();
Using Progress Changed Event handler you can show the progress to the user
_worker_ProgressChanged
I just tried to incorporate your code into something like this
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;
}
}
I have a form which when loaded starts a looping background worker which gets data from a usb device every half a second.
Once the program receives a new piece of data from the usb device it runs a function.
The _Dowork function has
while (true)
{
portConnection.Write("?");
Thread.Sleep(50);
}
I then have a separate routine that runs when data is received
private void portConnection_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
}
This is the routine that cannot then set values on the original form as the function is apparently on a separate thread.
How can I make this routine able to influence the original form?
Try something like this:
private void InvokeIfRequired(Control target, Delegate methodToInvoke)
{
if (target.InvokeRequired)
target.Invoke(methodToInvoke);
else
methodToInvoke.DynamicInvoke();
}
Call the method in your ProcessStatsReceived and in the methodToInvoke do your stuff...
You can use it like this in the ProccessStatusReceived:
InvokeIfRequired(this, new MethodInvoker(delegate() { this.lblStatus.Text = (string)e.Data; }));
The report progress part of BackgroundWorker is made for this.
This will make the DoWork method able to call a method on the GUI thread.
See this msdn article for details.
In short, the needed parts are:
Bind the progress changed handler:
bw.ProgressChanged += bw_ProgressChanged;
Set the BW to allow progress reporting:
bw.WorkerReportsProgress = true;
Implement the progress change method:
private void bw_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
//your logic to be executed on the GUI thread here
}
Then call it from DoWork like this:
bw.ReportProgress( progressPercentage, userState )
progressPercentage and userState can be user to transfer data from the background thread to the ProgressChanged method on the GUI thread.
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();
}
In my application I am using a timer to check for updates in an RSS feed, if new items are found I pop up a custom dialog to inform the user. When I run the check manually everything works great, but when the automatic check runs in the timers Elapsed event the custom dialog is not displayed.
First of all is this a thread issue? (I am assuming it is because both the manual and automatic check use the same code).
When I run the automatic check, do I have to invoke the method that runs the check from the Timers Elapsed event handler?
Is there something I need to do in my custom dialog class?
Edit:
this is a winforms application.
Here is an example of what the code is like. (Please don't point out syntax errors in this code example, this is just a simple example not real code).
public class MainForm : System.Windows.Forms.Form
{
//This is the object that does most of the work.
ObjectThatDoesWork MyObjectThatDoesWork = new ObjectThatDoesWork();
MyObjectThatDoesWork.NewItemsFound += new NewItemsFoundEventHandler(Found_New_Items);
private void Found_New_Items(object sender, System.EventArgs e)
{
//Display custom dialog to alert user.
}
//Method that doesn't really exist in my class,
// but shows that the main form can call Update for a manual check.
private void Button_Click(object sender, System.EventArgs e)
{
MyObjectThatDoesWork.Update();
}
//The rest of MainForm with boring main form stuff
}
public class ObjectThatDoesWork
{
System.Timers.Timer timer;
public ObjectThatDoesWork()
{
timer = new System.Timers.Timer();
timer.Interval = 600000;
timer.AutoReset = true;
timer.Elapsed += new new System.Timers.ElapsedEventHandler(TimeToWork);
timer.Start();
}
private void TimeToWork(object sender, System.Timers.ElapsedEventArgs e)
{
Update();
}
public void Update()
{
//Check for updates and raise an event if new items are found.
//The event is consumed by the main form.
OnNewItemsFound(this);
}
public delgate void NewItemsFoundEventHandler(object sender, System.EventArgs e);
public event NewItemsFoundEventHandler NewItemsFound;
protected void OnNewItemsFound(object sender)
{
if(NewItemsFound != null)
{
NewItemsFound(sender, new System.EventArgs());
}
}
}
After reading some of the comments and answers, I think my problem is that I am using a System.Timers.Timer not a System.Windows.Forms.Timer.
EDIT:
After changing to a Forms.Timer initial testing looks good (but no new items exist yet so have not seen the custom dialog). I added a bit of code to output the thread ID to a file when the update method is called. Using the Timers.Timer the thread ID was not the GUI thread, but using the Forms.Timer the thread ID is the same as the GUI.
Which timer are you using? System.Windows.Forms.Timer automatically fires the event on the UI thread. If you are using other one you will need to use Control.Invoke to call the method on UI thread.
You should use Forms.Timer here, or if you use other kind of timers, serialize calls to UI with .Invoke()
Is your application a WPF-Application? If so, you must delegate the work from your background-thread to the Dispatcher associated with the UI thread.
Post some code, so you can get better help and have a look at the Dispatcher class http://msdn.microsoft.com/en-us/library/system.windows.threading.dispatcher.invoke.aspx
private static System.Threading.SynchronizationContext _UI_Context;
//call this function once from the UI thread
internal static void init_CallOnUIThread()
{
_UI_Context = System.Threading.SynchronizationContext.Current;
}
public static void CallOnUIThread(Action action, bool asynchronous = false)
{
if (!asynchronous)
_UI_Context.Send((o) =>
{
action();
}, null);
else
_UI_Context.Post((o) =>
{
action();
}, null);
}