I'm running a C# forms application which starts a thread to acquire some data. This thread has some events inside it i.e: the events fire in the thread and are supposed to be captured by the same thread. However, the thread's events don't seem to be firing. Any clues?
private void btnPlay_Click(object sender, EventArgs e)
{
Thread thread = new Thread(kinect.onlineRun);
thread.IsBackground = true;
thread.Start();
}
inside the thread:
void PointCreated(object sender, IdEventArgs e) // a certain event that should fire and it doesn't
{
Console.WriteLine("Event Fired!");
}
public void onlinerun()
{
Console.WriteLine("run started"); // this is printed on console
while (true)
{
do_some_work();
//this work could result in the PointCreated event firing
}
}
Give something like this a try:
Assuming your calling class is called Controller and your delegate is called ControlEventHandler...
private void PointCreated(object sender, IdEventArgs e)
{
// Ensure the event was received in the calling thread
if (this.InvokeRequired)
{
if (e != null)
{
// We aren't in the correct thread so pass on the event
this.BeginInvoke(new Controller.ControllerEventHandler(this.PointCreated), new object[] { sender, e });
}
}
else
{
lock (this)
{
Console.WriteLine("Event Fired!");
// TODO: Do some stuff here
}
}
}
Related
Here is my code depicts issue:
public partial class Form1 : Form
{
private readonly object lockObject = new object();
public Form1()
{
InitializeComponent();
}
private void btnWorkerThread_Click(object sender, EventArgs e)
{
Task.Factory.StartNew(() => SomeLongRunningThread());
}
private void SomeLongRunningThread()
{
lock (lockObject)
{
Thread.Sleep(3000);
if (txtResult.InvokeRequired)
{
Thread.Sleep(3000);
txtResult.Invoke((MethodInvoker) delegate { txtResult.Text = DateTime.Now.ToShortTimeString(); });
}
else
{
Thread.Sleep(3000);
txtResult.Text = DateTime.Now.ToShortTimeString();
}
}
}
private void btnUIThread_Click(object sender, EventArgs e)
{
SomeLongRunningThread();
}
}
When worker thread started and then btnUIThread clicked deadlock occurs:
It seems that deadlock occurs on txtResult.InvokeRequried:
Worker thread pass control to ui thread
As ui thread has the turn, it both updates txtResult and try to start btnUIThread click.
Deadlock occurs on line txtResult.Invoke((MethodInvoker) delegate { txtResult.Text = DateTime.Now.ToShortTimeString(); });
Correct me if I am wrong.
The question is how to handle deadlock?
Edit: After valuable answers from fellows, my real projects is more complicated as expected, and lock block has more codes that I should guarantee executed by one thread at a time.
To avoid a deadlock you should lock differently. There's no point in locking all that threading code.
Moreover this lock (lockobject) is not necessary because in your sample SomeAction() is already synchronized to execute in the main/UI thread.
private void SomeLongRunningThread()
{
Thread.Sleep(3000);
if (txtResult.InvokeRequired)
{
Thread.Sleep(3000);
txtResult.Invoke((MethodInvoker) delegate { SomeAction });
}
else
{
Thread.Sleep(3000);
SomeAction();
}
}
private void SomeAction(){
// This lock is not needed as long this method is only called from SomeLongRunningThread()
//lock (lockObject)
//{
txtResult.Text = DateTime.Now.ToShortTimeString();
//}
}
There is no need to lock here at all.
Calling txtResult.Invoke((MethodInvoker) delegate { txtResult.Text = DateTime.Now.ToShortTimeString(); }); pushes the delegate on to the UI message loop queue. When the loop is ready it pops the delegate and executes. The UI can only run one thing at once - No race and no deadlock.
.NET 4.5 introduced the IProgress< T> interface to report progress from threads and tasks, and the Progress< T> implementation which runs a callback or raises an event in the thread that created it. With this, and async/await, you don't need to call BeginInvoke or `Invoke.
This code can be simplified by using Progress :
public partial class Form1 : Form
{
IProgress<string> _progress;
public Form1()
{
InitializeComponent();
_progress = new Progress<string>(UpdateUI);
}
void UpdateUI(string message)
{
txtResult.Text = message;
}
private void btnWorkerThread_Click(object sender, EventArgs e)
{
Task.Run(() => SomeLongRunningThread());
}
private void SomeLongRunningThread()
{
Thread.Sleep(3000);
_progress.Report(DateTime.Now.ToShortTimeString());
}
private void btnUIThread_Click(object sender, EventArgs e)
{
SomeLongRunningThread();
}
}
It's a C# winform .Net framework 2.0 project: The time-consuming network transferring jobs run in a BackupgroundWorker. In this backgroundwork thread, SynchronizationContext method is used to send the current job title to a message dialog in the main UI thread. However, in my code, thread racing problem happens and "NullReferenceException" is given sometime (not always, but randomly). Here is my code:
private void DoBtn_Click(object sender, EventArgs e)
{
m_msgForm = new MsgForm(); //m_msgForm is a member variable of the class, and MsgForm is a form class with a "public static SynchronizationContext synContext"
m_msgForm.UpdateMsg("starting ..."); //UpdateMsg is public method to show progress information
BackgroundWorker myBackgroundWorker = new BackgroundWorker();
myBackgroundWorker.DoWork +=new DoWorkEventHandler(myBackgroundWorker_DoWork);
myBackgroundWorker.RunWorkerCompleted +=new RunWorkerCompletedEventHandler(myBackgroundWorker_RunWorkerCompleted);
myBackgroundWorker.RunWorkerAsync(theBackgroundArgument);
m_msgForm.ShowDialog(); //show as a modal dialog
}
And in the Background worker thread:
private void myBackgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
//... some code
string msgText ="doing job: " +job.Title;
RestoreMsgForm.synContext.Send(m_msgForm.UpdateMsg, msgText); //send message to m_msgForm in the UI thread
//... some code
}
The problem is : sometime the "RestoreMsgForm.synContext.Send() in the backgroundworker thread will run before m_msgForm.ShowDialog() in the UI thead.
And in this case, NullReferenceException will be throw.
How to solve this problem? thanks.
the definition of RestoreMsgForm is
public partial class RestoreMsgForm : Form
{
public static SynchronizationContext synContext;
public RestoreMsgForm()
{
InitializeComponent();
}
private void RestoreMsgForm_Load(object sender, EventArgs e)
{
synContext = SynchronizationContext.Current;
}
public void UpdateMsg(object msg)
{
msgLabel.Text = (string)msg;
}
}
See if it works like this instead:
private void DoBtn_Click(object sender, EventArgs e)
{
m_msgForm = new MsgForm(); //m_msgForm is a member variable of the class, and MsgForm is a form class with a "public static SynchronizationContext synContext"
m_msgForm.UpdateMsg("starting ..."); //UpdateMsg is public method to show progress information
BackgroundWorker myBackgroundWorker = new BackgroundWorker();
myBackgroundWorker.DoWork += new DoWorkEventHandler(myBackgroundWorker_DoWork);
myBackgroundWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(myBackgroundWorker_RunWorkerCompleted);
myBackgroundWorker.WorkerReportsProgress = true;
myBackgroundWorker.ProgressChanged += new ProgressChangedEventHandler(myBackgroundWorker_ProgressChanged);
myBackgroundWorker.RunWorkerAsync(theBackgroundArgument);
}
private void myBackgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker worker = (BackgroundWorker)sender;
worker.ReportProgress(-1);
//... some code
string msgText = "doing job: " + job.Title;
worker.ReportProgress(0, msgText);
//... some code
worker.ReportProgress(0, "...other text...");
//... some code
worker.ReportProgress(0, "...etc...");
}
void myBackgroundWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
if (e.ProgressPercentage == -1)
{
m_msgForm.ShowDialog(); //show as a modal dialog
}
else
{
m_msgForm.UpdateMsg(e.UserState.ToString);
}
}
*Note that we have to set .WorkerReportsProgress = true for the BackgroundWorker() so we can use ReportProgress() and receive the ProgressChanged() event.
I can't use eventArgs from a method trigged for a second Thread:
public class MovilinkCommunication
{
//Method Declarations
public delegate void MovilinkWatchParametersEventMethod(ParameterAddress sender, MovilinkEventArgs e);
private MovilinkWatchParametersEventMethod onWatchParameterMethod;
//class constructor
//here, the user inputs the method (in main thread) that desires to call in
//parameter changed moment
public MovilinkCommunication(MovilinkWatchParametersEventMethod userOnWatchParameterMethod)
{
//assign user method (in main thread) to wach variables
onWatchParameterMethod = userOnWatchParameterMethod;
//start communication thread (second thread)
Thread movilinkThread = new Thread(new ThreadStart(movilinkIOManagerThread));
movilinkThread.Start();
}
.
.
.
//create delegates with "sender" parameter and "e" conditions of call
delegate void CallOnWatchParameterMethod(ParameterAddress sender, MovilinkEventArgs e);
private void callOnWatchParameterMethod(ParameterAddress sender, MovilinkEventArgs e)
{
//calling user method in main thread with event args obtained in
//communication thread (second thread)
onWatchParameterMethod(sender, e);
}
.
.
.
//communication thread
private void movilinkIOManagerThread()
{
ParameterAddress sender;
MovilinkEventArgs e;
.
.
.
while (movilinkAccessor.OperationStatusOk)
{
.
.
.
CallOnWatchParameterMethod thdCallOnWatchParameterMethod =
new CallOnWatchParameterMethod(callOnWatchParameterMethod);
Dispatcher.CurrentDispatcher.Invoke(thdCallOnWatchParameterMethod, new object[] { sender, e });
.
.
.
}
}
}
Works fine, but when I try use "sender" and "e" event args in user method (in main thread), the message bellow appears:
"The calling thread cannot access this object because a different thread owns it."
Can someone give me a hint about this problem? Thanks,
Jeferson
Follow Tudor, thanks again. This code is in window.xaml.cs code. The code in first post is in MovilinkComunication.cs.
MovilinkCommunication comunicadorMovilink;
private void wndPrincipal_Loaded(object sender, RoutedEventArgs e)
{
//creating communication object, setting the desired event
//to be trigged in secundary thread
comunicadorMovilink =
new MovilinkCommunication(getChangeParameters_Movilink);
}
.
.
.
//desired method to made actions in window, if detected
//change of parameters in external hardware
private void getChangeParameters_Movilink(ParameterAddress sender, MovilinkEventArgs e)
{
//error occurs here. Any code with GUI return error.
label24.Content = e.ActualValue.ToString();
}
The label update needs to be done via Dispatcher.BeginInvoke:
private void getChangeParameters_Movilink(ParameterAddress sender, MovilinkEventArgs e)
{
label24.Dispatcher.BeginInvoke(
(Action)(() =>
{
label24.Content = e.ActualValue.ToString();
}));
}
if your application is winforms, you can do this
public void d()
{
if (this.InvokeRequired)
{
BeginInvoke( new MethodInvoker( delegate() {
foo(a, b);
} ) );
}
else
{
foo(a, b);
}
}
private void foo(int a, int b)
{
}
in this example, d and foo are located in the form's class
Thanks a lot, this works fine
if (this.InvokeRequired)
{
BeginInvoke(new MethodInvoker(delegate()
{
printausfueren();
}));
}
else
{
printausfueren();
}
In the main thread I have a Timer. In the Tick event I run a BackgroundWorker. I do some things there and after that BackgroundWorker calls RunWorkerCompleted event.
In the main thread I have function Stop. This function disables the Timer. But I want wait for BackgroundWorker when he is working.
For example:
public void Next()
{
// Start the asynchronous operation
if (!this._backgroundWorker.IsBusy)
this._backgroundWorker.RunWorkerAsync();
}
private void _backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
DoSomething();
}
private void _backgroundWorker_RunWorkerCompleted(object sender,
RunWorkerCompletedEventArgs e)
{
DoSomethingElse();
}
public void Stop()
{
this._timer.Enabled = false;
}
So my question is how wait for RunWorkerCompleted event of BackgroundWorker? I need to wait until DoSomethingElse(); is finished.
Thanks
Handle the BackgroundWorker.RunWorkerCompleted event which occures when the background operation has completed, has been canceled, or has raised an exception.
// This event handler deals with the results of the
// background operation.
private void backgroundWorker1_RunWorkerCompleted(
object sender, RunWorkerCompletedEventArgs e)
{
// First, handle the case where an exception was thrown.
if (e.Error != null)
{
}
else if (e.Cancelled)
{
// Next, handle the case where the user canceled
// the operation.
// Note that due to a race condition in
// the DoWork event handler, the Cancelled
// flag may not have been set, even though
// CancelAsync was called.
}
else
{
// Finally, handle the case where the operation
// succeeded.
}
}
If you only require two threads, allow the thread that called this._backgroundWorker.RunWorkerAsync();
to die after it calls this method and call anything you want to occur after DoSomethingElse(); within the same block as below
private void _backgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
DoSomethingElse();
DoSomethingAfterSomethingElse();
}
Otherwise you are halting a thread to start another and then returning, which defeats the purpose of multiple threads?
I think BackgroundWorker.IsBusy property is the only member that can help you in this case. Hope below logic will do what you need.
//Add a class member
private bool stopped;
public void Stop()
{
if (!this._backgroundWorker.IsBusy)
{
this._timer.Enabled = false;
stopped = false;
}
else
{
stopped = true;
}
}
private void _backgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
DoSomethingElse();
if (stopped)
{
this._timer.Enabled = false;
stopped = false;
}
}
Here is a way to stop/freeze the main thread until your background worker finishes:
public void Stop()
{
if (!_backgroundWorker.IsBusy)
{
_timer.Enabled = false;
// Stop/Freeze the main thread until the background worker finishes
while (_backgroundWorker.IsBusy)
{
Thread.Sleep(100);
}
}
}
Now if your application uses a form, I would just disable the whole form and show message letting the user know the the application is waiting for the process to finish. You can also have flag to disable the form from closing.
private bool _canClose;
public void Stop()
{
if (!_backgroundWorker.IsBusy)
{
_timer.Enabled = false;
// Don't let the user do anything in the form until the background worker finishes
this.IsEnabled = false;
_label.Text = "Waiting for the process to finish";
_canClose = false;
}
}
private void _backgroundWorker_RunWorkerCompleted(object sender,
RunWorkerCompletedEventArgs e)
{
DoSomethingElse();
// Allow the user to close the form
this.IsEnabled = true;
_canClose = true;
}
private void MainWindow_Closing(object sender, CancelEventArgs e)
{
e.Cancel = !_canClose;
}
using System;
using System.Windows.Forms;
using agsXMPP;
using System.Text;
namespace iTalk2
{
public partial class Main : Form
{
agsXMPP.XmppClientConnection objXmpp;
public Main()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
Console.WriteLine("Logging in. Please wait...");
Console.ReadLine();
objXmpp = new agsXMPP.XmppClientConnection();
agsXMPP.Jid jid = null;
jid = new agsXMPP.Jid("username" + "#gmail.com");
objXmpp.Password = "password";
objXmpp.Username = jid.User;
objXmpp.Server = jid.Server;
objXmpp.AutoResolveConnectServer = true;
try
{
objXmpp.OnMessage += messageReceived;
objXmpp.OnAuthError += loginFailed;
objXmpp.OnLogin += loggedIn;
objXmpp.Open();
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
Console.ReadLine();
}
}
private void messageReceived(object sender, agsXMPP.protocol.client.Message msg)
{
string[] chatMessage = null;
chatMessage = msg.From.ToString().Split('/');
agsXMPP.Jid jid = null;
jid = new agsXMPP.Jid(chatMessage[0]);
agsXMPP.protocol.client.Message autoReply = null;
autoReply = new agsXMPP.protocol.client.Message(jid, agsXMPP.protocol.client.MessageType.chat, "This is a test");
objXmpp.Send(autoReply);
}
private void loginFailed(object o, agsXMPP.Xml.Dom.Element el)
{
Console.WriteLine("Login failed. Please check your details.");
}
private void loggedIn(object o)
{
Console.WriteLine("Logged in and Active.");
lblStatus.Text = "Online";
}
private void txtUsername_TextChanged(object sender, EventArgs e)
{
}
private void label1_Click(object sender, EventArgs e)
{
}
private void label2_Click(object sender, EventArgs e)
{
}
private void txtPassword_TextChanged(object sender, EventArgs e)
{
}
private void btnlogin_Click(object sender, EventArgs e)
{
}
}
}
This code is not working. the function 'loggedIn(object o)' is not working. it says the lblStatus (which is a label) is on another thread. the error window says "Cross-thread operation not valid: Control 'lblStatus' accessed from a thread other than the thread it was created on." thanks in advance.
You need to invoke a call on the UI thread. If you add code as follows at the top of the loggedIn method it should work:-
if(InvokeRequired)
{
Invoke(new Action<object>(loggedIn), o);
return;
}
WinForms is designed such that controls must only be manipulated on the UI-thread, the thread that runs the message-loop that manages the control.
Try this instead:
private void loggedIn(object o)
{
Console.WriteLine("Logged in and Active.");
Action act = () => lblStatus.Text = "Online";
Invoke(act);
}
If your application is such that this method can be called on the UI thread or a separate worker thread, you'd be better off testing forInvokeRequired(simply: am I on the control's UI thread?) and dealing with the result appropriately. For example,
private void loggedIn(object o)
{
if(InvokeRequired)
Invoke(new Action<object>(loggedIn), o);
else
{
Console.WriteLine("Logged in and Active.");
lblStatus.Text = "Online";
}
}
Note that Invokewill block until the UI-update is completed. If you want something more fire-and-forget, use BeginInvokeinstead.
When you start an application it is running from a single thread. This is the main thread, sometimes called the UI thread (since the UI will usually be rendered at startup and as a consequence it will be on that main thread.
Now, when you listen to events, your methods/delegates will get called from new threads. This is a consequence of the event based design. Normally this is not a problem unless you are trying to share data between two threads. This is exactly what happens with your UI elements. In this case your UI elements were created by your first thread but other threads are trying to update its value.
Given your design, you should check for IsInvokeRequired on the control and if so, use Invoke to set the new value. This will marshal your call from the new thread into the main thread that your UI is running on and will allow you to safely change the control.