开发者

Threading Method Question

I'm using the fol开发者_JS百科lowing method to show a modeless Message Box.

public void ShowMessageBox(string Message)
{
  var thread = new Thread(
    () =>
    {
      MessageBox.Show(Message);
    });
  thread.Start();
}

The "() => {...}" is something I've never seen before. What is the name for this code pattern?

Also, thread.Start starts the thread, and it automatically closes once the "()=>{...}" method completes (when the Message Box is OK'ed), right? If so, can you please point me to some official documentation saying that the thread closes automatically?

Thanks!


It's the lambda operator, and read as "goes to". MSDN has a good intro: Lambda Expressions (C# Programming Guide)

One concern with your example is that you're spinning up a new thread to update the UI, the UI is intrinsically single-threaded, so background updates are generally the wrong thing to do (unless you're manually/explicitly checking InvokeRequired and calling Invoke() as needed.


Regarding the UI threading...

In WinForms every Form or Control is created on a particular thread (the "UI Thread"), and you can think of that thread as owning that control (not exactly correct, but a good way to conceptualize it). Updating the UI from that thread is safe, updating the UI from another thread runs the risk of collisions and corruption and all the usual risks of parallel/async programming.

...So... how do you safely update the UI from a background thread without blocking the UI? In short--you can't--the best you can do is block it for the bare minimum required to update the UI. This is where InvokeRequired and Invoke() come in...

Here's a sample: you should be able to drop this into the code-behind of a new form with a button and textbox.

To use:

  • Try commenting out either the call to SetTextAsyncSafe() or SetTextAsyncSafe() -- running both could confuse you since they won't necessarily execute in the order they're called (they're running async, remember?).

  • Then set a breakpoint on SetText(). You should see the "safe" call will actually call the method twice--the first call will detect InvokeRequired and will call the method a 2nd time for the correct thread by Invoke()'ing to it.

  • You should see an Exception thrown when SetTextAsyncUnsafe() actually gets to the textBox1.Text = value; statements. The exception will be an InvalidOperationException with a message stating "Cross-thread operation not valid" -- you can google this term for more details.

The code:

private void button1_Click(object sender, EventArgs e)
{
  SetTextAsyncSafe("This update was made from the UI Thread by using Invoke()");
  SetTextAsyncUnsafe("This update was made directly from the background thread and can cause problems");
}

private void SetTextAsyncUnsafe(string value)
{
  new Thread(() => SetText(value, false)).Start();
}

private void SetTextAsyncSafe(string value)
{
  new Thread(() => SetText(value, true)).Start();
}

private void SetText(string value, bool checkInvokeRequired)
{
  if (checkInvokeRequired) 
  {
    if (InvokeRequired) 
    {
      Invoke(new Action(() => SetText(value, checkInvokeRequired)));
      return; // early exit
    }
  }

  textBox1.Text = value;
}


That is a Lambda. In this case, you're using it to create a new anonymous method that will be run when the new Thread is started.

It's the (near) equivalent of:

public void ShowMessageBox(string Message)
{
    var thread = new Thread(ShowBox);
    thread.Start(Message);
}

public void ShowBox(object message)
{
    MessageBox.Show(message.ToString());
}


This is called a Lambda Expression. You can read more here.


Lambda expression, C# version 3 feature.

Don't use this code. A message box needs a parent window, something it can make sure to be on top of. It can normally find a parent by itself by iterating the windows that were created on the same thread. Not in this case though, there are no other windows, it has to pick the desktop window as the parent.

That will go wrong badly when the user is working in an app window or switches focus to another app, the message box disappears behind the foreground window. There is no obvious way for the user to tell that it is there, she'll just loses sight of it. It could be hours, if not days, before she finds it back. That thread is meanwhile consuming resources badly, you would probably never consider it if you knew that this message box requires a megabyte of memory. In extreme cases, you'll crash the program with OOM.

The common alternative in Windows UI programming is a balloon tooltip provided by a NotifyIcon. Or your own form with the TopMost property set to True so it cannot easily get lost. Also allows you to control the position, important for "non-modal" notifications that should not get in the way. Set that form's ShowWithoutActivation property to true in the form constructor so it doesn't steal the focus.


Its a statement lambda.

Yes, thread is active as long as this anonymous method is running. Since after MessageBox.Show() there is no other statements, thread will exit, and this must be true... if you are in doubt add this before start:

thread.Name = "LALALA";

And then debug your app. When the message box apear, pause execution, go to Threads View and you will see LALALA running. Click OK and pause again, there should be no "LALALA"... =)

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜