multi thread issue

2019-07-16 07:51发布

问题:

I'm using a timer to reset a lable I use as a warning box. Basically, if the user does something (more specifically, something goes wrong, ex : He uses a word not recognized by the program), this catches what went wrong early and returns to him what happened so he can change the input.

The reset blanks out the label after 5 seconds to prevent him from seeing something like "please do not use chinese characters" and maybe still thinking an old error is still up. This is what I got reading the invoke (since I hear begininvoke requires an endinvoke, I chose invoke).

private void lblWrn_TextChange(object sender, EventArgs e)
{
    Timee = new System.Timers.Timer(5000);
    Timee.Elapsed += new ElapsedEventHandler(timerClearWrn);
    Timee.Enabled = true;
}

string empty = "";
private void timerClearWrn(object sender, ElapsedEventArgs elapsed)
{
    lblWrn.Invoke(new Action<Label>(lblWrn), new object[] { lblWrn, "" });
}

I am not too sure where I am going wrong with this, and looking up examples, cannot figure out which part to change. Can someone explain to me the error or invoke a bit more?

回答1:

If it's a Windows Forms application, use System.Windows.Forms.Timer, then you don't need Invoke, as the timer callback is executed on the main thread.

Also, don't create a new timer on every text change.



回答2:

Actually, Control.BeginInvoke does not need an EndInvoke; it is Delegate.BeginInvoke that does.

First, I would also recommend using a Windows.Forms.Timer, since it looks like you are using winforms - that will automatically fire on the UI thread, making all the problems go away - just run the code you want to run in the handler (don't use Invoke etc)

The problem in your example is that the parameters don't match; an Action<> expects a method name (more accurately: a method group) to be invoked, and the parameters in the array must be suitable. Since you don't show the method you plan to invoke, I can't help there - but lblWarn isn't a method (it is a field).



回答3:

on this line lblWrn.Invoke(new Action(lblWrn), new object[] { lblWrn, "" });

shouldn't the bold part be a function and not a object?



回答4:

You have a few options. Option 1 is a little clunky. Options 2 and 3 are better.

Option 1: Continue with general strategy of using Control.Invoke but use code that calls Invoke correctly, disable auto resetting of the timer, and removes the event handler.

private void lblWrn_TextChange(object sender, EventArgs e)
{
    var Timee = new System.Timers.Timer(5000);
    Timee.Elapsed += this.timerClearWrn;
    Timee.AutoReset = false; // Raise the Elapsed event only once
    Timee.Enabled = true;
}

private void timerClearWrn(object sender, ElapsedEventArgs elapsed)
{
    lblWrn.Invoke(
      (MethodInvoker)(()=>
      {
        lblWrn.Text = "";
      }), null);
    var Timee = (System.Timers.Timer)sender;
    Timee.Elapsed -= this.timerClearWrn;
}

Option 2: Use a System.Windows.Forms.Timer instead of System.Timers.Timer.

Option 3: Use the SynchronizingObject property of System.Timers.Timer. This is my preferred option when timers are to be created and used dynamically from a UI thread.

private void lblWrn_TextChange(object sender, EventArgs e)
{
    var Timee = new System.Timers.Timer(5000);
    Timee.Elapsed += this.timerClearWrn;
    Timee.AutoReset = false; // Raise the Elapsed event only once
    Timee.SynchronizingObject = this; // Tell the Timer to raise the Elapsed event on the UI thread
    Timee.Enabled = true;
}

private void timerClearWrn(object sender, ElapsedEventArgs elapsed)
{
    lblWrn.Text = "";
    var Timee = (System.Timers.Timer)sender;
    Timee.Elapsed -= this.timerClearWrn;
}