I'm using the ThreadPool to manage my threads. Separately from the UI thread I have a thread that does data retrieval and general work operations and I have a 3rd thread that updates the UI to reflect the status of requested operations.
See code below:
// ui thread
private void btnLoadClients_Click(object sender, EventArgs e)
{
// start thread 1
ThreadPool.QueueUserWorkItem(new Form1().LoadClientList);
}
// thread 1
private void LoadClientList(object state)
{
ThreadBusy = true;
ThreadAction = "Loading Clients...";
// start thread 2
ThreadPool.QueueUserWorkItem(new Form1().ShowProgress);
// get data
ClientController c = new ClientController();
List<Client> clients = c.GetClient();
foreach (Client item in clients)
{
cmbClientList.Items.Add(item.Name);
}
cmbClientList.Items.Insert(0, "Please select a client");
ThreadBusy = false;
}
// thread 2
private void ShowProgress(object state)
{
while (ThreadBusy)
{
foreach (string action in lstAction.Items)
{
// write the action that's being taken to the listbox
if (String.Compare(action, ThreadAction) != 0)
lstAction.Items.Add(ThreadAction);
}
}
}
Problem is that although ShowProgress is being hit when I set a breakpoint on it, execution isn't entering it really. The while (ThreadBusy) line isn't getting hit ever.
Have I got anything wrong here?
ThreadPool.QueueUserWorkItem(new Form1().LoadClientList);
ThreadPool.QueueUserWorkItem(new Form1().ShowProgress);
You're creating new Form1 instances every time you start a background thread, every action you take in these methods will happen to these new, "unbounded" instances, not on the one interacting with the user.
If you want to perform background work in WinForms you can leverage the BackgroundWorker class.
A really simple example:
public static class Program
{
public static void Main()
{
var backgroundWorker = new BackgroundWorker();
backgroundWorker.WorkerReportsProgress = true
backgroundWorker.Disposed += BackgroundWorker_Disposed;
backgroundWorker.DoWork += BackgroundWorker_DoWork;
backgroundWorker.ProgressChanged += BackgroundWorker_ProgressChanged;
backgroundWorker.RunWorkerCompleted += BackgroundWorker_RunWorkerCompleted;
backgroundWorker.RunWorkerAsync();
}
private static void BackgroundWorker_Disposed(object sender, EventArgs e)
{
// Cleanup after yourself.
}
private static void BackgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
// Do your things in background.
}
private static void BackgroundWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
// Notify progress.
}
private static void BackgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
// The background task is complete ("successfully" is NOT implied).
}
}
ThreadBusy property that you set to true belongs to a different Form1 object. because the thread that runs ShowProgress executes on a new instance of Form1 and its ThreadBusy property is false always. Thats why it is not entering into the while loop.
Can you try this
ThreadPool.QueueUserWorkItem(this.ShowProgress);
instead of
ThreadPool.QueueUserWorkItem(new Form1().ShowProgress);
Related
I am sending a string value containing some data I need in my C# application with help of a TCP Server/Client connection. The string value changes 200-300x per second.
Now, what I just want to do for now is to show the incoming strings values in a label.
I've read about using a BackgroundWorker class for that case, since it would freeze the UI otherwise. But unfortunately, it doesn't work! I first tested the connection in a simple console application, there it worked perfectly. Do you may see what I'm doing wrong here?
namespace test
{
public partial class Form1 : Form
{
BackgroundWorker worker;
IPAddress ip;
Socket s;
TcpListener tcp;
public Form1()
{
InitializeComponent();
ip = IPAddress.Parse("1.2.3.4");
tcp = new TcpListener(ip, 8001);
worker = new BackgroundWorker();
worker.DoWork += new DoWorkEventHandler(TCPServer);
worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(workerCompleted);
}
string data = string.Empty;
int k = 0;
byte[] b = new byte[4096];
private void TCPServer(object sender, DoWorkEventArgs e)
{
while (true)
{
k = s.Receive(b);
data = Encoding.ASCII.GetString(b, 0, k);
e.Result = data.ToString();
}
}
private void workerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
label1.Text = e.Result.ToString();
}
private void button1_Click(object sender, EventArgs e)
{
tcp.Start();
s = tcp.AcceptSocket();
worker.RunWorkerAsync();
}
}
}
Your problem is, that the workerCompleted method will only be called when the backgroundWorker finished. Since your TCPServer Method has an infinite loop that will never happen.
You could use the ReportProgress functionality of your backgroundWorker. But I don't think it's a good idea to update your label 200-300 times per second at all.
The BackgroundWorker will just run your task on another thread you need to raise events periodically to update the ui
Your workerCompleted method is used to update label1.Text but that method will only be invoked when the worker job finished and it looks like it's not meant to finish.
So there are a couple of things you can do:
You can register a ProgressChanged event handler to the worker and call worker.ReportProgress(progress, data) in the DoWork handler (your TCPServer method):
// Add to init
worker.WorkerReportsProgress = true;
worker.ProgressChanged = new ProgressChangedEventHandler(WorkerProgressChanged);
private void TCPServer(object sender, DoWorkEventArgs e)
{
while(true)
{
k = s.Receive(b);
data = Encoding.ASCII.GetString(b, 0, k);
int progress = 0; // Or the actual progress if available
object param = data.ToString();
worker.ReportProgress(progress, param);
}
}
private void workerProgressChanged(object sender, ProgressChangedEventArgs e)
{
label1.Text = e.Result.ToString();
}
Or you could remove the while(true) cycle from the work method and just run it once, then in the workerCompleted method start the worker again:
private void TCPServer(object sender, DoWorkEventArgs e)
{
k = s.Receive(b);
data = Encoding.ASCII.GetString(b, 0, k);
e.Result = data.ToString();
}
private void workerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
label1.Text = e.Result.ToString();
worker.RunWorkerAsync();
}
You can also look for inspiration here How to update GUI with backgroundworker?
I have a feeling the you will need to move the following stuff to the worker thread:
TcpListener tcp.Start();
Socket s = tcp.AcceptSocket();
These look like an async actions that will block the ui thread
What I would actually do in your shoes:
Since you basically need a server running the whole time and you already have it working as a console application. I would find a way to run the application and subscribe to its output and display the info in another ui only application.
Or at least run the entire server part in a separate process/thread and send updates to the ui.
I think the intent of the BackgroundWorker is to run some tasks that would otherwise block the UI in another thread, but it's not supposed to run an endless task.
So I'm making a C# app which has to continuously read and display the contents of a text file, while allowing the user to enter something into a text box and append it to the end of that very file.
I'm doing this by running my read method on a separate thread, however changing the variable which stores the display text-files contents is what's causing a problem. Initially I tried having a method which did this, however that's not working and gave a 'cross-thread-operation-not-valid' error. I then tried applying some code I found on MSDN, but now after updating the variable once the thread ended!
Please help.
partial class MainForm
{
delegate void SetTextCallback(string text);
public static string msg;
public static string name;
public void InitClient()
{
name = "public.txt";
Console.WriteLine(name);
if(!File.Exists(name))
{
File.Create(name);
File.AppendAllText(name, "Welcome to " + name);
}
Thread Read = new Thread(new ThreadStart(this.Client));
Read.Start();
while(!Read.IsAlive);
}
public void WriteText()
{
File.AppendAllText(name, this.InputBox.Text);
this.InputBox.Clear();
}
private void SetText(string text)
{
if (this.OutPut.InvokeRequired)
{
SetTextCallback d = new SetTextCallback(SetText);
this.Invoke(d, new object[] { text });
}
else
{
this.OutPut.Text = text;
}
}
public void Client()
{
msg = File.ReadAllText(name);
Console.WriteLine(msg);
Thread.Sleep(300);
this.SetText(msg);
}
}
Why is the thread behaving like this. How can I modify my code so that the contents of the output box always equals that of the text file.
Any suggestions welcome.
You've got multiple problems here,
the use of the File is probably not thread-safe.
your method does not repeat
your are Sleep()ing on a Thread
You can solve all of them by ditching the Thread and use a simple Timer.
Try using a background worker instead of creating a new thread. The background worker will run its content in a seperate thread, and allows you to report 'progress' while its working. This progress report will always be run on the UI-thread (or the thread which started the background worker).
It also has an event which is called when the background worker is finished. This is also run on the UI thread.
This example should get you started.
Update: Added some very basic error handling as suggested
The idea is to use the UserData (2nd argument) of ReportProgress to do updates on the UI thread whenever you need to. In this case it is a string, but this can be any object.
Furthermore, you can use the Result of the DoWorkEventArgs to produce a final result from the background work. In this case, I return any exception which was thrown, or null otherwise, but you can return whatever you want here as well.
It is, as Henk mentioned in his comment, very important to handle errors that occur inside the DoWork callback, because exceptions etc which occurs here will be swallowed and the worker will complete as if nothing bad happened.
private BackgroundWorker _backgroundWorker;
public Form1()
{
InitializeComponent();
_backgroundWorker = new BackgroundWorker();
_backgroundWorker.WorkerReportsProgress = true;
_backgroundWorker.WorkerSupportsCancellation = true;
// This is the background thread
_backgroundWorker.DoWork += BackgroundWorkerOnDoWork;
// Called when you report progress
_backgroundWorker.ProgressChanged += BackgroundWorkerOnProgressChanged;
// Called when the worker is done
_backgroundWorker.RunWorkerCompleted += BackgroundWorkerOnRunWorkerCompleted;
}
private void BackgroundWorkerOnRunWorkerCompleted(object sender, RunWorkerCompletedEventArgs runWorkerCompletedEventArgs)
{
if (runWorkerCompletedEventArgs.Result != null)
{
// Handle error or throw it
throw runWorkerCompletedEventArgs.Result as Exception;
}
textBox1.Text = "Worker completed";
}
private void BackgroundWorkerOnProgressChanged(object sender, ProgressChangedEventArgs progressChangedEventArgs)
{
textBox1.Text = progressChangedEventArgs.UserState as string;
}
private void BackgroundWorkerOnDoWork(object sender, DoWorkEventArgs doWorkEventArgs)
{
try
{
for (int i = 0; i < 100 && !_backgroundWorker.CancellationPending; i++)
{
_backgroundWorker.ReportProgress(0, i + " cycles");
Thread.Sleep(100);
}
}
catch (Exception ex)
{
doWorkEventArgs.Result = ex;
}
}
private void startButton_Click(object sender, EventArgs e)
{
if (!_backgroundWorker.IsBusy)
_backgroundWorker.RunWorkerAsync();
}
private void cancelButton_Click(object sender, EventArgs e)
{
if(_backgroundWorker.IsBusy)
_backgroundWorker.CancelAsync();
}
I'm using a BackgroundWorker to periodically check a hardware switch. Due to it is connected via a slow RS485 network, I have to delay the next status update.
On switch Status change I want to update an OK/nOK Picture Box. This is realized as a green OK pictureBox over a nOK pictureBox. No real work is done here.
For expandability I decided to use the Backgroundworker. Finally I want to have a hidden worker, which
provides globally the Status of three switches and
updates on StatusChange the PictureBoxes.
Problem description
Once the BackgroundWorker is started, it works as expected. However the GUI freezes.
What did I try?
The MSDN BackgroundWorker Class Note 1
says, that GUI should be updated via ProgressChanged. I tried to raise this Event by Worker_Switch.ReportProgress(fakeProgress++) and failed. The PictureBox wasn't updated anymore.
Snippet from designer
this.Worker_Switch = new System.ComponentModel.BackgroundWorker();
//
// Worker_Switch
//
this.Worker_Switch.WorkerSupportsCancellation = true;
this.Worker_Switch.DoWork += new System.ComponentModel.DoWorkEventHandler(this.Worker_Switch_DoWork);
Snippet from Main Form
delegate void SetEventCallback(object sender, DoWorkEventArgs e); // Threadsafe calls for DoWork
private void btnBackgroundworker_Click(object sender, EventArgs e)
{
if (!Worker_Switch.IsBusy)
{
Worker_Switch.RunWorkerAsync();
}
}
private void Worker_Switch_DoWork(object sender, DoWorkEventArgs e)
{
// Worker Thread has no permission to change PictureBox "pictureBoxSwitchrightOK"
// Therefore this method calls itsself in the MainThread, if necessary.
while (!Worker_Switch.CancellationPending)
{
if (this.pictureBoxSwitchrightOK.InvokeRequired) // Worker Thread
{
System.Threading.Thread.Sleep(400);
SetEventCallback myCall = new SetEventCallback(Worker_Switch_DoWork);
this.Invoke(myCall, new object[] { sender, e });
}
else // Main Thread
{
// Turns OK Picture Box invisible, if nOk State (Switch pushed)
pictureBoxSwitchrightOK.Visible = SwitchOK("right"); // true: OK (green)
this.Refresh();
}
}
private bool SwitchOK(string rightOrLeft) // select one of the switches
{ (...)} // gets hardware switch status
Edit: Special Thanks to laszlokiss88 (3 possibilities) and JMK (for simplicity with System.Windows.Forms Timer from toolbox)
This alternative from Toolbox also worked:
this.timer_Switch.Enabled = true;
this.timer_Switch.Interval = 400;
this.timer_Switch.Tick += new System.EventHandler(this.timer_Switch_Tick);
private void timer_Switch_Tick(object sender, EventArgs e)
{
motorSwitchControl.Init(); // globally available Switch status
SwitchRight = SwitchOK("right");
SwitchRightOK.Visible = SwitchRight;
SwitchLeft = SwitchOK("left"); // globally available Switch status
SwitchLeftOK.Visible = SwitchLeft;
SwitchAllOK = SwitchRight & SwitchLeft;
this.Refresh();
}
a) Is it correct, that the Sleep() actually happens in the Worker Thread?
- no Main Thread
b) What is going wrong, if I manipulate user interface objects in DoWork? (Contrary to MSDN Note)
- works in Main Thread?
c) What is the correct way to periodically update a PictureBox? DoWork, ProgressChanged, RunWorkerCompleted...?
- Three possibilities from laszlokiss88 answer.
You can update the UI from the DoWork event via the Dispatcher, or Control.Begininvoke(winforms), or you can do it via the ProgressChanged event of the BackgroundWorker:
public MainWindow()
{
InitializeComponent();
var bw = new BackgroundWorker();
bw.WorkerReportsProgress = true;
bw.DoWork += new DoWorkEventHandler(bw_DoWork);
bw.ProgressChanged += new ProgressChangedEventHandler(bw_ProgressChanged);
bw.RunWorkerAsync();
}
void bw_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
// You are in the main thread
// Update the UI here
string data = (string)e.UserState;
}
void bw_DoWork(object sender, DoWorkEventArgs e)
{
// You are in a worker thread
(sender as BackgroundWorker).ReportProgress(0, "right");
}
For a start you should almost never have a need to put an active background thead to sleep. I am also not sure why you are constructing/defining the delegate this way, try some thing like
public delegate void UpdatePictureBox();
myDelegate = new UpdatePictureBox(UpdatePictureboxMethod);
then you have a method UpdatePictureBoxMethod
private void UpdatePictureBoxMethod()
{
this.pictureBox1.Image = Properties.Resources.SomeImage;
}
or something simalar, where you pass in the image to update to.
Alternatively you could use the (bgWorker as BackgroundWorker).ReportProgress(progress, object); method. So from the background thread you call
(bgWorker as BackgroundWorker).ReportProgress(progressBarValue, infoBall);
where here class IfoBall will hold all your important information
class InfoBall
{
public int nProgressBar { get; set; }
public int nMaxProgressBar { get; set; }
public Image image { get; set; }
}
then you can pass this object back to the UI thread and do your updates
void bgWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
// On UI thread.
InfoBall someBall = (InfoBall)e.UserState;
this.pictureBox1.Image = someBall.image;
// etc...
}
I hope this helps.
I need to detect a procedure from a click event has finished without delaying the main wpf process..
What I don't want
public void click_event(object sender,routedeventargs)
{
<launch a bunch of threads>
while(<threads.are alive>);
<code for what i want to do after threads are done>
}
public void threadtask()
{}
what i just did
public void click_event()
{
foreach(<thread>)
<give thread task and start() each>
}
}
but this will not detect when the threads are done.. need help here. Thanks.
You are asking for two different things. You want the main thread to not be blocked, but you want to do something when the other threads are done (which you have to wait for). Consider starting the threads from a new thread, then let that other thread do the work. Something like this:
public void click_event()
{
<start new thread>
<foreach(thread in threads)>
<do work>
<join threads>
<do your work here>
}
So all of the work is on a different thread, even the work you want to do afterward. Given that, do you need more than one worker thread anyway?
Check out this article
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.ClickMe.Click += new RoutedEventHandler(ClickMe_Click);
}
void ClickMe_Click(object sender, RoutedEventArgs e)
{
BackgroundWorker bw = new BackgroundWorker();
bw.DoWork += (workSender, workE) =>
{
string argument = (string)workE.Argument;
// argument == "Some data"
System.Threading.Thread.Sleep(2000);
};
bw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bw_RunWorkerCompleted);
bw.RunWorkerAsync("Some data");
}
void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
this.ResultsTextBlock.Text = "I'm done";
}
}
I have a program that makes some hefty calls to the database and then updates the UI. This is causing problems because for most of the time it means that the UI in not responsive. I therefore decided that I wanted to put the function calls that access the database and update the UI in a separate thread, so now I have something like this:
private delegate void CallAsyncDelegate();
private void CallGetDBValues()
{
// Call GetDatabaseValues in new thread
CallAsyncDelegate callGetDatabaseValues = new
CallAsyncDelegate(GetDatabaseValues);
BeginInvoke(callGetDatabaseValues);
}
private void GetDatabaseValues()
{
// Get lots of data here
// Update UI here
}
...
However, it seems to make no difference whatsoever to the UI. I read somewhere that if the code to be run in a separate thread needed to update the UI then this was how the call should be made - is this correct? Am I doing something wrong?
You may be better served using the BackgroundWorker that is built-in to the .NET framework.
BackgroundWorker bw = new BackgroundWorker();
bw.DoWork += new DoWorkEventHandler(bw_DoWork);
bw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bw_RunWorkerCompleted);
bw.ProgressChanged += new ProgressChangedEventHandler(bw_ProgressChanged);
bw.WorkerReportsProgress = true;
void bw_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
// update UI with status
label1.Text = (string)e.UserState
}
void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
//Check for cancel
if(e.Cancelled)
{
//Handle the cancellation.
{
//Check for error
if(e.Error)
{
//Handle the error.
}
// Update UI that data retrieval is complete
}
void bw_DoWork(object sender, DoWorkEventArgs e)
{
// Get data
//foreach to process data
//Report progress
bw.ReportProgress(n, message);
}
Here's a link to the MSDN article on how to use the BackgroundWorker for additional details. Thanks to Henk Holterman for the suggestion to include this:
http://msdn.microsoft.com/en-us/library/cc221403%28VS.95%29.aspx
In the "// Update UI here", make sure to use Control.Invoke to actually do the work -- it's imperative that the UI only be "touched" by the UI-thread, and this only happens when you use Control.Invoke.
BeginInvoke and Invoke means to run the code on the UI thread. In this case if you are calling CallGetDBValues() from the UI thread you are not going to gain anything.
Usually you will create a BackgroundWorker or background thread that will do the heavy lifting and it will Invoke back to the UI thread the values that need to be updated.
A BackgroundWorker will probably be the better solution (see Robaticus's answer), but here is a background thread version.
private delegate void CallAsyncDelegate();
private void button_Click( object sender, EventArgs e )
{
Thread thread = new Thread( GetDBValues );
thread.IsBackground = true;
thread.Start();
}
private void GetDBValues()
{
foreach( ... )
{
Invoke( new CallAsyncDelegate( UpdateUI ) );
}
}
private void UpdateUI()
{
/* Update the user interface */
}
I'm not sure of the syntax.. but the sytax I'm more familiar with is something like:
public delegate object myDelegate(object myParam);
Public class MyClass
{
public static void Main()
{
myDelegate d = new myDelegate(myMethod);
d.BeginInvoke ( new object() );
}
static void myMethod(object myParam)
{
// do some work!!
return new object);
}
}