I am currently working on a application that checks emails from an email-account via IMAP. This function is called every 5 seconds and it needs some time to work through.
private void CheckForRequests()
{
List<string[]> mails = CollectAllMails();
for (int i = 0; i < mails.Count; i++)
{
if (mails[i][0].Split('_')[0] == "request")
{
//INVITATION TO ME
if (mails[i][0].Split('_')[2] == username && mails[i][0].Split('_')[3] == "request")
{
DeleteMail(mails[i][0]);
MessageBoxResult result = MessageBox.Show("Do you accept the request from " + mails[i][0].Split('_')[1], "Invitation", MessageBoxButton.YesNo, MessageBoxImage.Question, MessageBoxResult.Yes);
if (result == MessageBoxResult.Yes)
{
DeleteMail("request_" + mails[i][0].Split('_')[1] + "_" + mails[i][0].Split('_')[2] + "_" + mails[i][0].Split('_')[3]);
SendMail("request_" + mails[i][0].Split('_')[1] + "_" + mails[i][0].Split('_')[2] + "_accept", "");
ChatWindow chat = new ChatWindow();
chat.ShowDialog();
//do open chat window
}
else if (result == MessageBoxResult.No)
{
DeleteMail("request_" + mails[i][0].Split('_')[1] + mails[i][0].Split('_')[2]);
SendMail("request_" + mails[i][0].Split('_')[1] + "_" + mails[i][0].Split('_')[2] + "_decline", "");
}
}
//ACCEPTION FROM ANOTHER DUDE
else if (mails[i][0].Split('_')[2] != username && mails[i][0].Split('_')[3] == "accept")
{
ChatWindow chat = new ChatWindow();
chat.ShowDialog();
}
//REJECTION FROM ANOTHER DUDE
else if (mails[i][0].Split('_')[2] != username && mails[i][0].Split('_')[3] == "decline")
{
MessageBox.Show("Your invitation was declined.", "Sorry", MessageBoxButton.OK, MessageBoxImage.Exclamation);
}
}
else if (mails[i][0].Split('_')[0] == "somethingelse")
{
}
}
}
My loop calls this method every 5 seconds and in this time I can't write or do anything in my application. Im pretty sure that I have to use a Thread or Task to solve the problem but I didn't found out how to do this related to my case. When I call the method in a Task and I click Yes it crashes and says something like it has to be a STA-Thread... In this case I don't even want to access the GUI with the thread, I just want to check the mails and if the method found something it should do something like break from the Task and call a method (NOT from the Task).
What would be the cleanest solution for this problem?
Your threading issue is caused by you trying to do UI stuff on a non-UI thread. You can solve this problem by using Dispatcher.Invoke whenever you call UI stuff like this
Application.Current.Dispatcher.Invoke(() =>
{
// Your stuff here
});
So in your case you'd have something like this
void CheckForRequests()
{
// Do stuff
Application.Current.Dispatcher.Invoke(() =>
{
// Open your message box
});
// Do more stuff
Application.Current.Dispatcher.Invoke(() =>
{
// Open another message box
});
}
Your are Right about needing to use Threads
While #Gareth is right about the need to use a dispatcher to correctly access elements across theads, i actually don't see any threading in your code, though the error message clearly proves you have attempted some.
to implement the threading you have various options
firstly you can do this directly via Tasks or the older Thread classes
this would simply be done like so
private void CheckForRequestsAsync()=> Task.Run(()=>CheckForRequests());
this will instantly create and start a task that will perform CheckForRequests in a separate thread freeing the GUI to continues its work on the GUI, however this is a very basic implementation and likely will need further enhancement before reliably meeting your needs
another option is to take advantage of some of the newer features in .Net and use the async keyword
if you declare CheckForRequests as private async void CheckForRequests (object sender, EventArgs e) then the void will be automatically be converted into a task which can be fired off by an event hander as a async task say of a Timer
eg
Timer timer = new Timer(5000);
timer.Elapsed += CheckForRequests; //where CheckForRequests has the async keyword
timer.Start();
combine this with the dispatcher information #Gareth suggested on anything that throws a cross thread access exception and you should be ready
this would look something like this:
MessageBoxResult result = Dispatcher.Invoke(() =>
MessageBox.Show("Do you accept the request from " + mails[i][0].Split('_')[1], "Invitation", MessageBoxButton.YesNo, MessageBoxImage.Question, MessageBoxResult.Yes);
);
Note if you use async with out using the await keyword then you will get warnings that your thread may exit before any worker threads have completed, however this is just a warning as if you aren't calling any worker threads inside your method or you don't need them to complete before exiting then there is no harm done
Finally
one of the comments suggested using DispatcherTimer rather than a Timer, i would not suggest this as every time the timer ticks, it will run your code in the GUI thread locking it just as you are already seen, the DispatcherTimer is best used when the timer heavily changes the GUI and is quick running
though if you redefined your code then you could user a DispatcherTimer by breaking out the gui element from the slow running process
dispatcherTimer = new System.Windows.Threading.DispatcherTimer();
dispatcherTimer.Tick += (s,e)=>{
if( MessageBox.Show("Do you accept the request", "Invitation", MessageBoxButton.YesNo, MessageBoxImage.Question, MessageBoxResult.Yes) == MessageBoxResult.Yes)
{
Task.Run(()=>CheckForRequests());
}
}
dispatcherTimer.Interval = new TimeSpan(0,0,1);
dispatcherTimer.Start();
Related
I know that the question may not make sense, and I'm having a tough time trying to think of a way to explain it, so I will show a snippet of code to help. I'm using Winforms on visual studio express 2010:
private void button1(object sender, EventArgs e)
{
txtOutput.Text += "Auto-collecting variables. This may take several minutes";
string v = foo();
txtOutput.Text += "\n" + v;
string b = bar();
txtOutput.Text += "\n" + b;
txtOutput.SelectionStart = txtOutput.Text.Length;
txtOutput.ScrollToCaret(); //scrolls to the bottom of textbox
}
So basically, when the user clicks button1, I want "Auto-collecting variables..." to be displayed in the textbox, and then have foo() execute, display that, and then have bar() execute, and then display that.
What is currently happening is that foo() and bar() execute, and then everything is displayed all at once after foo() and bar() have executed (functions that take several minutes). Is there anyway to fix this, or is there a work around?
Edit: Version of C# is 4.0. If I update to 4.5 or 5.0, will computers without .NET 4.5/5.0 be able to run the .exe?
C# 5.0 makes doing this trivial.
Execute the long running tasks in a background thread using Task.Run and use await to execute the rest of the method as a continuation in the UI thread without blocking the UI thread for the duration of the asynchronous task.
private async void button1(object sender, EventArgs e)
{
txtOutput.Text += "Auto-collecting variables. This may take several minutes";
string v = await Task.Run(() => foo());
txtOutput.Text += "\n" + v;
string b = await Task.Run(() => bar());
txtOutput.Text += "\n" + b;
txtOutput.SelectionStart = txtOutput.Text.Length;
txtOutput.ScrollToCaret(); //scrolls to the bottom of textbox
}
You can do the same in C# 4.0 like so: (The first solution will be transformed by the compiler into something similar.)
private void button1(object sender, EventArgs e)
{
txtOutput.Text += "Auto-collecting variables. This may take several minutes";
Task.Factory.StartNew(() => foo())
.ContinueWith(t => txtOutput.Text += "\n" + t.Result
, TaskScheduler.FromCurrentSynchronizationContext())
.ContinueWith(t => bar())
.ContinueWith(t =>
{
txtOutput.Text += "\n" + t.Result;
txtOutput.SelectionStart = txtOutput.Text.Length;
txtOutput.ScrollToCaret(); //scrolls to the bottom of textbox
}
, TaskScheduler.FromCurrentSynchronizationContext());
}
Use the BackgroundWorker class to do your processing without blocking UI updates. It has events that can be used to transfer progress information to the UI thread.
Depending on the version of .NET, you can use BackgroundWorker (Pre 4.0) or Tasks (Post 4.0 - 3.5 with an add-on)...to name a few.
Backgroundworker Pseudocode:
var backgroundWorker = new BackgroundWorker()
method
{
//Update UI
backgroundWorker.RunWorkAsync()
}
asyncworkmethod
{
//do main logic
}
asynccompletemethod
{
//Update UI to say done
}
Task Pseudocode:
method
{
//Update UI
TaskFactory.StartNew(()=>DoWork).ContinueWith((previousTask)=>UpdateUIToSayDone)
}
And, if you are using 4.5, then you can use the async/await keyword, however that is just syntactic sugar around tasks (mostly...). Servy already has a decent example of this, though if you go that approach
Using a background process (read the other answers) is the correct way to go but if you are looking at a very quick workaround you can call Application.DoEvents() after updating the TextBox. In most cases this call will result in your form updating to reflect the changes you made.
txtOutput.Update() should do what you want, but you should consider using background thread to complete long running task without blocking UI thread.
This is a follow up question to Updating a dialog from another form (The code and screenshots can be found there)
To solve my GUI hanging problem I received 2 recommendations:
Using Application.DoEvents()
Using a BackgroundWorker
The DoEvents() approach works, however it has been pointed out that I should not use it. Indeed, I notice that the GUI updates correctly but is unresponsive for short times.
That's why I want to use a BackgroundWorker and have read up on it.
I don't understand how I would implement it so that it can be used to update the 4 labels in my example code separately, though.
I want to show the progress (and update 4 dialog labels) as the program successfully finishes one job. The BackgroundWorker has only 1 DoWork() though. I have tried to use the e.Argument of the DoWorkEventArgs to differentiate between the different update methods but that attempt had failed.
public partial class BackgroundWorkerImportStatusDialog : Form
{
private BackgroundWorker dialogWorker = new BackgroundWorker();
private string path;
private string clientName;
public BackgroundWorkerImportStatusDialog()
{
InitializeComponent();
}
public void updateFileStatus(string path)
{
this.path = path;
dialogWorker = new BackgroundWorker();
dialogWorker.DoWork += new DoWorkEventHandler(updateLabels);
dialogWorker.RunWorkerAsync(UpdateComponent.FileStatus);
}
public void updatePrintStatus()
{
dialogWorker = new BackgroundWorker();
dialogWorker.DoWork += new DoWorkEventHandler(updateLabels);
dialogWorker.RunWorkerAsync(UpdateComponent.PrintStatus);
}
public void updateImportStatus(string clientName)
{
this.clientName = clientName;
dialogWorker = new BackgroundWorker();
dialogWorker.DoWork += new DoWorkEventHandler(updateLabels);
dialogWorker.RunWorkerAsync(UpdateComponent.ImportStatus);
}
public void updateArchiveStatus()
{
dialogWorker = new BackgroundWorker();
dialogWorker.DoWork += new DoWorkEventHandler(updateLabels);
dialogWorker.RunWorkerAsync(UpdateComponent.ArchiveStatus);
}
private void updateLabels(object sender, DoWorkEventArgs e)
{
MessageBox.Show(e.Argument.ToString());
if ((UpdateComponent) e.Argument == UpdateComponent.FileStatus)
{
t_filename.Text = path;
}
if ((UpdateComponent) e.Argument == UpdateComponent.PrintStatus)
{
t_printed.Text = "sent to printer";
}
if ((UpdateComponent) e.Argument == UpdateComponent.ImportStatus)
{
t_client.Text = clientName;
}
if ((UpdateComponent) e.Argument == UpdateComponent.ArchiveStatus)
{
t_archived.Text = "archived";
}
}
public enum UpdateComponent { FileStatus, PrintStatus, ImportStatus, ArchiveStatus}
And I can't imagine having 4 BackgroundWorkers for this pretty trivial dialog is the solution.
As I understand your question, you want to have your dialog form inform the user about 4 different aspects of your application running:
printing status
file status
import status
archiver status
Background worker could be used to periodically check each one. You may advanced progressbar by 25% after status of each operation is checked (and update your UI with appropriate information).
You may also try async programming - i.e. just start the operation, and lets your application continue. When the operation completes, your application will be notified, and could update information on the form.
Depending on the .NET framework you're using you may use async and await (avaialble since .NET 4.5 / C# 5 - async & await on MSDN) or classic approach to asynchronous programming.
Edit:
I am not sure that BackgroundWorker is the best solution in this situation. I can imagine having something like:
BackhgroundWorker checking things just once - i.e. check printing status once, file status once, import status once, archiver status once. This may sound silly, but it could be user behavior driver - i.e. explicitly launched when user clicks or invokes this mechanism any other way. ProgressBar could be put on the application's statausbar, so that user knows that 'application is actually doing something'.
Previous approach could be improved a bit - you never actually finish your job in BackgroundWorker - instead inside your main method you just have an infinite loop. This will allow you to check things periodically. In this approach there is no point in increasing the progress.
Sample for the second approach:
private void bg_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker worker = sender as BackgroundWorker;
for (int i = 1; i <= 10; i++)
{
if (worker.CancellationPending == true)
{
e.Cancel = true;
break;
}
else
{
CheckPrintingStatus();
CheckFileStatus();
CheckImportStatus();
CheckArchiverStatus();
System.Threading.Thread.Sleep(5000); // sleep for 5 seconds
}
}
}
There is a question if this solution (second approach) is better than having a thread created explicitly. You could think of creating 4 different threads, so that each could check something else. This would be a bit heavier on the OS, but on the other hand you can set different sleep times for every operation.
If you go for bare threads - you may want to use ThreadPool instead of creating threads explicitly.
I have a text block called "findListText". Here, I am updating the text in it:
private void InstantSearch(object sender, KeyEventArgs e)
{
if (e.Key == Key.Return)
{
HitEnter = true;
}
findListText.Text = "Processing request. Please wait...";
Find(bool.Parse("False" as string));
}
However, the next set of code is a search function that can take up to 10 seconds, and at the end of it, it changes the text in findListText again.
private void Find(bool? bForward = true)
{
{
//Lots and lots of code
}
findListText.Text = "Search completed."
}
The problem is, the textblock never seems to update to "Processing request. Please wait...". The textblock is in it's original state and 10 seconds later updates to "Search completed.", seemingly skipping out the middle man.
I'm using C# - WPF. What am I doing wrong here?
Doesn't matter what technology I think.
The code is running on the same thread, meaning the the UI won't be updated untill all the code on that thread is completed. You should address a different thread to update that textblock.
In that case, you will have 2 thread:
The origininal thread, executing the "lots and lots of code"
The second (extra) created thread, which will handle updating the textblock's text while the other thread is executing the other code.
I've created a little something that should resolve your problem, it's based on this Stack Overflow page
Since this is WPF, try the following: after changing the text to "Processgin", call:
Application.Current.Dispatcher.Invoke(DispatcherPriority.Background, new Action(delegate { this.UpdateLayout(); }));
This will tell the thread to update the UI as soon as possible.
Here is how to run your find method in its own thread.
private void InstantSearch(object sender, KeyEventArgs e)
{
if (e.Key == Key.Return)
{
HitEnter = true;
}
findListText.Text = "Processing request. Please wait...";
BackgroundWorker tempWorker = new BackgroundWorker();
tempWorker.DoWork += delegate
{
Find(bool.Parse("False" as string));
};
tempWorker.RunWorkerAsync();
}
If you try that, you'll get an error because you access your UI thread from the background thread. So you'll need to update your find method as well.
private void Find(bool? bForward = true)
{
{
//Lots and lots of code
}
Dispatcher.BeginInvoke((Action) delegate {
findListText.Text = "Search completed."
});
}
You should look into the UI threading concept of WPF. Invoke the Dispatcher to modify the textbox. Also the search should run with ThreadPool.QueueWorkerItem.
// Start worker thread
ThreadPool.QueueUserWorkItem(state =>
{
// Long running logic here
findListText.Dispatcher.BeginInvoke(() => findListText.Text = "Processing request. Please wait...");
Find(bool.Parse("False" as string));
// Tip: Run change on GUI thread from the worker using the dispatcher
findListText.Dispatcher.BeginInvoke(() => findListText.Text = "Search completed.");
});
I came back to this just now, and had another browse across the internet for similar problems. For something as simple as pushing a single message before a long running process occurs, I'm surprised no-one suggested "Application.DoEvents();". Yes, I know it has it's flaws, and my UI will still hang, but this suits me perfectly for my situation, at least.
I'm having a lot of trouble with my background worker. I have a background worker to upload pictures (sometimes over 100) to tumblr. Naturally, this is extremely repetitive.
My problems are this:
1) As soon or a few seconds after I start my thread, backgroundWorker1_RunWorkerCompleted is called, as shown by it printing the text within that method/event.
2) When I cancel the thread, it calls backgroundWorker1_RunWorkerCompleted, as shown by it printing the text within it, however, files continue to keep uploading. It seems like the thread still exists.
I can't figure out what I'm doing wrong, I've been searching for the past few days and tried so many different methods. Here is my code:
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker backgroundWorker = sender as BackgroundWorker;
var restClient = new RestClient("http://tumblr.com/api/write");
foreach (string item in queueBox.Items)
{
if (!backgroundWorker.CancellationPending)
{
var request = new RestRequest(Method.POST);
request.RequestFormat = DataFormat.Json;
request.AddParameter("email", usernameBox.Text);
request.AddParameter("password", passwordBox.Text);
request.AddParameter("type", "photo");
byte[] byteArray = File.ReadAllBytes(FolderName + "\\" + item);
request.AddFile("data", byteArray, FolderName + "\\" + item);
var newItemToAvoidClosure = item;
restClient.ExecuteAsync(request, response => { doneBox.Invoke(new UpdateTextCallback(this.UpdateText), new object[] { newItemToAvoidClosure }); });
}
else
{
e.Cancel = true;
}
}
}
And here is my RunWorkerCompleted
private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (!(e.Cancelled))
{ //if not cancelled
if (!(e.Error == null))
{ //if above true, see if theres an error
doneBox.Items.Add("\r\nAn Error Ocurred: " + e.Error.Message + "\r\n. However, what you uploaded may have been successful up until this error." );
}
else
{ //else there is no error which means it was 100% successful
doneBox.Items.Add("Finished uploading! Go and check out your stuff on Tumblr! ^_^" );
}
}
else //else the user DID press cancel
{
doneBox.Items.Add("You pressed Cancel - However, the photos before you pressed cancel have been uploaded." );
}
}
Now, i'll try and explain what I think is happening in my DoWork,
since the for loop is what loops each photo upload, the if statement is in there so it can constantly check if cancellationpending is true. If it is, set e.Cancel to true to end the thread.
Also, I'm using ExecuteAsync to upload, so I was hoping I would be able to terminate the thread mid upload, so that photo doesn't upload.
Okay, I'm sorry, I know that this has been discussed before. I just can't figure out what I'm doing wrong though.
By the way, this is how I attempt to cancel:
public void stopButton_Click(object sender, EventArgs e)
{
doneBox.Items.Add("You pressed the stop button.");
backgroundWorker1.CancelAsync();
}
Thank you all.
Two things:
Firstly, you're trying to access the UI from the background worker:
request.AddParameter("email", usernameBox.Text);
You shouldn't do that. It sounds like you should be setting up all the information the background worker needs first, then starting it.
Secondly, the fact that you're calling ExecuteAsync is exactly why your background worker is completing immediately, but then the files are continuing to upload. You're basically queueing a load of work for the rest client to do asynchronously.
While are you using ExecuteAsync in the first place? You're already within a background thread - why not just do the work synchronously?
I am using a background worker below an a form, and when i click a button it should generate a document, but the GUI hangs , i don't know why it does that, because i feel i m using the backgroundworker right.. can anybody helps in this ?
private void btn_GenerateRevDoc_Click(object sender, EventArgs e)
{
DOC_GenerateVersDocBackgroundWorker = new BackgroundWorker();
DOC_GenerateVersDocBackgroundWorker.WorkerReportsProgress = true;
DOC_GenerateVersDocBackgroundWorker.WorkerSupportsCancellation = true;
DOC_GenerateVersDocBackgroundWorker.DoWork += new DoWorkEventHandler(DOC_GenerateVersDocBackgroundWorker_DoWork);
DOC_GenerateVersDocBackgroundWorker.ProgressChanged += new ProgressChangedEventHandler(DOC_GenerateVersDocBackgroundWorker_ProgressChanged);
DOC_GenerateVersDocBackgroundWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(DOC_GenerateVersDocBackgroundWorker_RunWorkerCompleted);
System.Threading.Thread.CurrentThread.Priority = ThreadPriority.BelowNormal;
if (Db.docVersionHistory != null && Db.docVersionHistory.Count > 0)
{
SaveFileDialog sfd = new SaveFileDialog();
sfd.Title = "Export Review To";
sfd.Filter = "Word files (*.doc)|*.doc|All files (*.*)|*.*";
sfd.FilterIndex = 1;
sfd.FileName = "";
if (sfd.ShowDialog() == DialogResult.OK)
{
if (!DOC_GenerateVersDocBackgroundWorker.IsBusy)
DOC_GenerateVersDocBackgroundWorker.RunWorkerAsync(sfd.FileName);
}
}
else
{
MessageBox.Show("No Review Records were found!");
}
}
void DOC_GenerateVersDocBackgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
if (this.InvokeRequired)
{
Invoke(new MethodInvoker(delegate
{
DocumentsNavigator.GenerateWordRevisionHistoryDoc(DOC_GenerateVersDocBackgroundWorker, versionsList, Db, (string)(e.Argument));
}));
}
else
{
DocumentsNavigator.GenerateWordRevisionHistoryDoc(DOC_GenerateVersDocBackgroundWorker, versionsList, Db, (string)(e.Argument));
}
}
You don't quite grasp how the BackgroundWorker is supposed to be used, and what the Invoke method does.
The Invoke method causes the code be called on the UI thread. So, don't pass the DocumentsNavigator.GenerateWordRevisionHistoryDoc method through Invoke. RunWorkerAsync is fine. I don't know what types versionsList and Db are, but if they are UI objects, you may need to copy the values you need to a new variable. For example, if versionsList is a ListBox, you should copy the selected values to a new string[], and use that string[] as a parameter to your method.
Here's what you think you wanted to do:
Create new Background worker
initialize your background worker
disable the btn_GenerateRevDoc button
Show the SaveFileDialog
Start the BackgroundWorker (RunWorkerAsync)
On the ProgressChanged event, if you're displaying a progress bar or
something, you can update that, this time you do have to passe it
through the Invoke method.
And on the RunWorkerCompleted event, show a messagebox or something,
and enable the btn_GenerateRevDoc button again
Oh, and this line should absolutely be removed:
System.Threading.Thread.CurrentThread.Priority = ThreadPriority.BelowNormal;
Inside your background worker you are again forwarding all the work to UI thread that's why your UI is hanging
if (this.InvokeRequired)
{
//this executes the work on UI thread
Invoke(new MethodInvoker(delegate
{
DocumentsNavigator.GenerateWordRevisionHistoryDoc(DOC_GenerateVersDocBackgroundWorker, versionsList, Db, (string)(e.Argument));
}));
}
else
{
//it will also be executed on UI thread
DocumentsNavigator.GenerateWordRevisionHistoryDoc(DOC_GenerateVersDocBackgroundWorker, versionsList, Db, (string)(e.Argument));
}
}
when i click a button it should generate a document, but the GUI hangs
from your code I can see that you should enter a file name and click ok. Is there a save dialog being opened somewhere?
Try writing the same code without using the background worker. Does it still hang? Also, observed that the condition if (!DOC_GenerateVersDocBackgroundWorker.IsBusy) doesn't make sense as you are creating a new backgroundworker everytime button is clicked
The problem is that You are running the code that hangs the GUI in a worker thread but you do it in an Invoke method.
The Invoke method runs the code in the thread of the GUI and so it hangs.
If you absolutely must call DocumentsNavigator.GenerateWordRevisionHistoryDoc in the GUI thread I don't see how you can make this call without hanging the GUI.
Try to rethink your code so you don't have to run any code in the BackgroungWorker in an Invoke method.
You are doing something unsafe.
instead of Invoke try
if (this.InvokeRequired)
{ this.BeginInvoke(new MethodInvoker(delegate
{