I have been trying to determine the proper approach to manipulate the UI from an asynchronous task. My application has become cluttered with threaded Invokes and BeginInvokes. I am trying to alleviate this clutter as well as provide a bit more responsiveness on the UI by taking advantage of C# async and await.
Somewhere on the UI thread I initialize IProgress event handler and pass it to an asynchronous function called DoInfiniteWorkAsync. This function runs infinitely in a background task but often has the need to update portions of the UI.
private void Form1_Load(object sender, EventArgs e)
{
// Create a UIEventHandler on our UI thread.
Progress<string> UIEventHandler = new Progress<string>();
UIEventHandler.ProgressChanged += UIEventHandler_CommandRaised;
// Pass the UIEventHandler to our Asyncronous task.
DoInfiniteWorkAsync(UIEventHandler);
}
void UIEventHandler_EventRaised(object sender, string e)
{
string eventMessage = e;
// Update UI based on the event message.
}
My DoInfiniteWorkAsync function uses the passed in UIEventHandler to report UIEventMessages while running its task.
private async Task DoInfiniteWorkAsync(IProgress<string> UIEventHandler)
{
await Task.Run(() =>
{
// 24/7 running tasks.
// Sets a UIEventMessage to signal UI thread to do something.
UIEventHandler.Report(UIEventMessage);
});
}
Is this the proper way to be updating the UI thread from a long running background thread? The fact that my event handler datatype (IProgress) is specifically directed at progress reporting is making me feel like I'm missing the point.
To bring in data from one asynchronous thread to another you have to invoke it.
Define in your class a field of property:
string _readData = null;
Then fill the string with the eventMessage and call a method to invoke the data.
string eventMessage = e;
_readData = eventMessage;
Msg();
This is the method Msg():
private void Msg()
{
if (this.InvokeRequired)
{
this.Invoke(new MethodInvoker(Msg));
}
else
{
textBox2.Text = textBox2.Text + Environment.NewLine + " >> " + _readData;
}
}
Related
I have a thread which calls one of the methods, now this method executes a query which can take a very long time possibly 40 minutes or so to complete,
I want to give user a a choice to be able to cancel this operation (meaning stop the thread and stop the query to release database).
I should mention that I am developing WPF Application using .net 4.5, SQL SERVER DB and C#.
You should use backgroundworker, it is exactly what you want.
Eather drag and drop it from the toolbox or create it in code - behind. It supports Cancellation, reports progress, notifies when complete and know if it is running or not.
Here is an example.
void method(){
BackgroundWorker worker = new BackgroundWorker();
worker.RunWorkerCompleted += worker_RunWorkerCompleted;
worker.ProgressChanged += worker_ProgressChanged;
worker.DoWork += worker_DoWork;
worker.WorkerSupportsCancellation = true;
if(!worker.IsBusy)
{
worker.RunWorkerAsync();
}
}
void worker_DoWork(object sender, DoWorkEventArgs e)
{
//do whatever needs to be done on the other thread here.
object argument = e.Argument; //if passed argument in RunWorkerAsync().
object result = new object();
e.Result = result;
//after making worker global, you can report progress like so:
worker.ReportProgress(50); //you can also pass a userState, which can be any object, to show some data already.
}
void worker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
//you can update a progress bar in here
int progress = e.ProgressPercentage;
}
void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
//when done
}
void CancelTheTask()
{
if (worker.IsBusy)
{
//make worker global first, but then
worker.CancelAsync();
}
}
A important things to look at: Never use resources in the DoWork method that are not created inside it. Thus pass things you need in the background worker as Arguments. And things that are created by the backgroundworker should not be set to a global variable ether, pass by result.
When cancelling, RunWorkCompleted will also be fired. Now the query to the database is already being executed, so that is still running, even when your application lost all resources to it.
To cancel that, we would need to know how you execute the query, like #S.Akbari mentioned is one way. Entity Framework 6 also supports cancellation.
For that: check this when using Queryable
here is another example
Or this solution without Entity Framework.
Using Task Parallel Library (TPL) you can use the Task Cancellation pattern.
When you have your Thread blocked on waiting for the query, it's useless for stopping anything.
Make sure the SqlConnection of the query is accessible from your UI and Close it. Abandon the Thread, it will terminate (with an error you've got to suppress).
If the UI thread is doing a Long-time operation it won't be able to process
UI requests. This is also known as Not Responding.
Use ThreadPool like this:
CancellationTokenSource ct;//instantiate it before ThreadPool.QueueUserWorkItem line
private void operation_Click(object sender, RoutedEventArgs e)
{
ct = new CancellationTokenSource();
ThreadPool.QueueUserWorkItem(_ =>
{
var result = LongTimeOperation();//set the operation in another thread so that the UI thread is kept responding
//use the Dispatcher to "return" to the UI thread
Dispatcher.BeginInvoke(new Action(() =>
{
//Use result for example : Label1.Text = result.ToString();
}));
});
}
To give user a choice to be able to cancel the operation use CancellationTokenSource like this:
private void cancel_Click(object sender, RoutedEventArgs e)
{
if (ct != null)
{
ct.Cancel();
ct= null;
}
}
Note: in LongTimeOperation() you must have one more parameter of type CancellationToken
private float LongTimeOperation(CancellationToken ct)
{
if (ct.IsCancellationRequested)
return -1;
....
....
}
This link is useful about Cancellation in Managed Threads.
this is a common problem.But in WPF and WinForm, i'd like to use BackGroundWorker. See Here
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'm pretty new to C# and all the threading stuff and I'm getting "Cross-threaded operation not valid error" at the moment.
Here are the relevant parts of the code:
private LinkedList<string> _statusList = new LinkedList<string>();
private void ReportToStatus(string message)
{
_statusList.AddLast(message);\
// textStatus is a textbox.
// And this is the exact line that is giving the error:
textStatus.Lines = _statusList.ToArray();
}
private void RunTest()
{
// ...
// Run the test in the background worker.
bgwTest.RunWorkerAsync(testCase);
}
private void bgwTest_DoWork(object sender, DoWorkEventArgs e)
{
TestCase testCase = e.Argument as TestCase;
// ...
// Run the test.
switch (testCase.TestType)
{
case "TestA": TestA(testCase);
break;
}
e.Result = testCase;
}
private void TestA(TestCase testCase)
{
// ...
PrintStatistic(statisticsForCoil, testCase.OutputFile);
}
}
private void PrintStatistic(int[] statistics, string outputFile)
{
// ...
ReportToStatus(result);
}
How should I proceed?
It looks like there might be a problem in _statusList. You can't write to it from a different thread, only read.
From MSDN
"The LinkedList class does not support chaining, splitting, cycles,
or other features that can leave the list in an inconsistent state.
The list remains consistent on a single thread. The only multithreaded
scenario supported by LinkedList is multithreaded read operations."
Also, you can't access the UI from a background thread. You need to use the dispatcher to invoke operations onto the UI thread. To do this your code will need to look like this
WPF
Application.Current.Dispatcher.Invoke(new Action(delegate
{
textStatus.Lines = _statusList.ToArray();
}));
WinForms
textStatus.Invoke(new Action(delegate
{
textStatus.Lines = _statusList.ToArray();
}));
The BackgroundWorker has a dedicated mechanism for updating the UI:
BackgroundWorker.ReportProgress. For example, in your code it could look like this:
private void ReportToStatus(string message)
{
_statusList.AddLast(message);
// textStatus is a textbox.
// And this is the exact line that is giving the error:
bgwTest.ReportProgress(0, _statusList.ToArray());
}
//Assuming this is the method handling bgwTest's ProgressChanged event
private void bgwTest_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
textStatus.Lines = (string[])(e.UserState);
}
You are trying to update the UI from your background worker which will cause that exception. You can use the Dispatcher to schedule an update instead - or more ideally use the background worker to do "background" work only, then do your UI updates when the RunWorkerCompleted event is raised.
I have a program that has stock quotes pushed to me via an API. The program also has a front end, made in XAML, that freezes while this program is running (i.e. processing the information that the API is sending me). I've tried using Dispatcher.Invoke and/or BackgroundWorker and have read up on threading plenty, but can't get it to unfreeze. Perhaps I'm just doing something wrong. I've attached the relevant code here. Was hoping someone could help.
private void QuoteUpdate(QuoteInfo info)
{
BackgroundWorker bwQuoteUpdate = new BackgroundWorker();
bwQuoteUpdate = new BackgroundWorker();
bwQuoteUpdate.WorkerSupportsCancellation = true;
bwQuoteUpdate.DoWork += bwQuoteUpdate_DoWork;
bwQuoteUpdate.RunWorkerAsync(info);
}
private void bwQuoteUpdate_DoWork(object sender, DoWorkEventArgs e)
{
try
{
Dispatcher.Invoke(DispatcherPriority.Normal, new ThreadStart(() =>
{
QuoteInfo info = e.Argument as QuoteInfo;
//logical functions and work are here
}));
}
catch (Exception ex)
{
System.Windows.Forms.MessageBox.Show("Error in QuoteUpdate: " + ex.Message, "Exception Thrown");
}
}
Although you’re creating a BackgroundWorker with the intention of executing your long-running task on a background thread, you’re still dispatching all your processing back onto the UI thread.
private void bwQuoteUpdate_DoWork(object sender, DoWorkEventArgs e)
{
// Code here runs on background thread.
Dispatcher.Invoke(DispatcherPriority.Normal, new ThreadStart(() =>
{
// Code here runs on UI thread.
}));
}
What you need to do is first perform your calculations on the background thread, but do not update any UI components; rather, store all your results in local variables. Then, once that’s done, use the Dispatcher.Invoke to dispatch control back to the UI thread, and use the results stored in your local variables to update your UI.
For example:
private void bwQuoteUpdate_DoWork(object sender, DoWorkEventArgs e)
{
// Code here runs on background thread.
QuoteInfo info = e.Argument as QuoteInfo;
string result = PerformLongRunningProcessing(info);
Dispatcher.Invoke(DispatcherPriority.Normal, new ThreadStart(() =>
{
// Code here runs on UI thread.
this.resultTextBox.Text = result;
}));
}
Yes, you are doing something wrong. The computation should be done in thread alone add only UI changes should be done in Dispatcher.Invoke.
And if you use DataBinding through INotifyPropertyChange, then drop the Dispatcher.Invoke completly, because marshaling the changes to UI thread is done automaticaly.
Try
Dispatcher.BeginInvoke(...)
I have a class to launch background operations in a WinForms application. I need to write this background worker since my requisites are using .NET 1.1, so I cannot use BackgroundWorker, that is only available from .NET 2.0
This class get a delegate and execute it in a thread. I want the main thread to respond to events.
I also want to indicate that the operation is running setting the application cursor to Cursors.WaitCursor.
What do you think about current implementation? I'm interested in the method WaitTillThreadFinishes(), because I'm not sure about Application.DoEvents(), please read the code and share with me opinions about WaitTillThreadFinishes.
The following code executes the operation:
private object ExecuteOperation (Delegate target, params object[] parameters)
{
mTargetDelegate = target;
mTargetParameters = parameters;
mTargetThread = new Thread(new ThreadStart(ThreadProc));
mTargetThread.Name = mTargetDelegate.Method.Name;
mOperationFinished = false;
// start threaded operation
mTargetThread.Start();
// perform active waiting
WaitTillThreadFinishes();
return mTargetResult;
}
The following code is executed in a thread, simply call the delegate, and wrap exceptions:
protected virtual void ThreadProc()
{
try
{
mTargetResult = mTargetDelegate.DynamicInvoke(mTargetParameters);
}
catch (ThreadAbortException) { }
catch (Exception ex)
{
//manage exceptions here ...
}
finally
{
mOperationFinished = true;
}
}
And this is the code performs an active waiting. I'm interested on share with you. Any better option? Any pain calling Application.DoEvents() massively?
private void WaitTillThreadFinishes ()
{
// Active wait to respond to events with a WaitCursor
while (!mOperationFinished)
{
// sleep to avoid CPU usage
System.Threading.Thread.Sleep(100);
Application.DoEvents();
Cursor.Current = Cursors.WaitCursor;
}
Cursor.Current = Cursors.Default;
}
Thanks in advance.
Please let me know if i understood your question correctly.
Why dont you use an event to notify the UI that the worker finished his job?
This way, the UI doen't get blocked by the worker, and you avoid busy waiting.
Sample Implementation
public class MyBackgroundWorker
{
// Fields
private Delegate _target;
private object[] _arguments;
// Events
public event EventHandler RunWorkerStarted;
public event EventHandler<RunWorkerCompletedEventArgs> RunWorkerCompleted;
// Event Invocators
public void InvokeRunWorkerStarted()
{
var handler = RunWorkerStarted;
if (handler != null) handler(this, new EventArgs());
}
public void InvokeRunWorkerCompleted(object result)
{
var handler = RunWorkerCompleted;
if (handler != null) handler(this, new RunWorkerCompletedEventArgs(result));
}
public void RunWorkerAsync(Delegate target, params object[] arguments)
{
_target = target;
_arguments = arguments;
new Thread(DoWork).Start(arguments);
}
// Helper method to run the target delegate
private void DoWork(object obj)
{
_target.DynamicInvoke(_arguments);
// Retrieve the target delegate's result and invoke the RunWorkerCompleted event with it (for simplicity, I'm sending null)
InvokeRunWorkerCompleted(null);
}
}
internal class RunWorkerCompletedEventArgs : EventArgs
{
public RunWorkerCompletedEventArgs(object result)
{
Result = result;
}
public object Result { get; set; }
}
Usage
In the UI you can use it this way:
private void button1_Click(object sender, EventArgs e)
{
var worker = new MyBackgroundWorker();
worker.RunWorkerStarted += worker_RunWorkerStarted;
worker.RunWorkerCompleted += worker_Completed;
worker.RunWorkerAsync(new MethodInvoker(SomeLengthyOperation), null);
}
void worker_RunWorkerStarted(object sender, EventArgs e)
{
}
void worker_Completed(object sender, EventArgs e)
{
MessageBox.Show("Worker completed");
}
private void SomeLengthyOperation()
{
Thread.Sleep(5000);
}
Final Notes
Remember to Invoke() in the event handlers to access the UI thread correctly. You can also modify the worker so this is done in a safe way.
There isn't much support in 1.1 for doing this, but I'll tell you what I'd do (sorry, no code at this time).
As for the asynchronous operation, I'd use the APM to kick off and complete the asynchronous method. This is fully supported in 1.1, so no worries there.
The idea is that in the UI, you store some indication that work is being done (a boolean field, for example) and (optionally) a Timer used to "wake up" the UI on a regular basis to check on the current status of the background work and indicate this to the user.
You would set the boolean to indicate you are working in the background, call BeginInvoke() on your delegate (using the overload that takes a callback search for "Executing a Callback Method When an Asynchronous Call Completes
"), and start the Timer. When the user attempts to use the UI, you would optionally check the boolean and cancel the operation, thus preventing the user from doing something harmful while you are waiting. When the timer Ticks, you can check the status of your asynchronous method by, say, a shared field that the method writes updates to and the UI reads. For example, a double which the UI uses to update a progress bar.
Once the callback fires, you clean up your asynchronous mess (i.e., call EndInvoke, and handle any exceptions thrown, etc), turn off the Timer and reset your boolean running indication field.
By using this method, you can keep the UI completely responsive (and partially usable, depending on your overall design), can set up a mechanism to abort the background worker (through the use of another field, the reverse of the boolean mentioned earlier, and inform the user of the status of the operation.
There is occasionally a case for kicking off a thread and waiting for its return, if you are doing other things in the meantime, but in this case, with the code you have shown, it is meaningless.
If you want the threadProc to allow for events to be processed, then call doevents in that, which will free up the CPU briefly, allowing for processing.
Unless you have a particular reason for needing to thread processes, you should not do it. Getting it right - as Ian Boyd has said - is difficult, and the more you need to interact with it the harder it is. If you can run fire-and-forget threads, that is the easiest.
Ideally you start the asynchronous operation and leave your form alone (aside from maybe using the Cursors.AppStarting cursor).
When your threaded operation completes, it then needs to fire some sort of BackgroundOperationComplete event. This is where your would call from your asynchronous delegate code:
form.Invoke(BackgroundOperationComplete);
The form's BackgroundOperationComplete method is where you can handle the fact that the background operation is complete:
void BackgroundOperationComplete()
{
this.Cursor = Cursors.DefaultCursor;
lblAnswer.Text = "The thread is done";
}
If all else fails, keep the operation synchronous, and use an IProgressDialog. (brief conceptual pseudo-code from memory):
void DoStuff()
{
IProgressDialog pd = new ProgressDialog();
pd.SetTitle = "Calculating Widgets";
pd.StartTimer(PDTIMER_RESET, NULL)
pd.StartProgressDialog(this.Handle, NULL, PROGDLG_MODAL | PROGDLG_NOTIME | PROGDLG_NOPROGRESSBAR | PROGDLG_NOCANCEL, NULL);
try
{
pd.SetLine(1, "Please wait while the widgets are frobbed");
DoTheThingThatDoesTheSynchronousStuff();
}
finally
{
pd.StopProgressDialog();
}
pd = null;
}