5

I'm modifying an application written in C# that makes heavy-use of multi-threading to play audio files and display images to a user. Given that it is multi-threaded, I need to use the Invoke method often to change form elements. I'm running into a pattern that I'm not very comfortable with, where I find myself writing frequent, small, delegate methods that typically only do one thing. An example of this is as follows:

delegate void setImageCallback(Image img);
private void setImage(Image img)
{
    this.pictureBox1.Image = img;
}

private void someOtherMethod()
{
    ...
    if (this.pictureBox1.InvokeRequired)
    {
        this.Invoke(new setImageCallback(setImage), Image.FromFile("example.png");
    }
    else
    {
        this.pictureBox1.Image = Image.FromFile("example.png");
    }
    ...
}

How do people generally handle these situations, so that you don't find yourself writing an absurd number of delegates and methods just to remain thread-safe? Obviously, consolidation of similar methods is great, but if I potentially need to update every form element on my form, I don't want to have a "modify" delegate and method for each of these.

Thanks.

rybosome
  • 5,046
  • 7
  • 44
  • 64

3 Answers3

8

A good example is here.

this.BeginInvoke( (Action) (()=>
    {
        pictureBox1.Image = Image.FromFile("example.png");
    }));
Jason Down
  • 21,731
  • 12
  • 83
  • 117
  • Instructions to do a search doesn't really make for a good answer. If you just edit and take out the "do a google search" part, this would be better. – Samuel Neff May 30 '11 at 01:47
  • Ok removed it... I was going to type out an example, but am in the middle of cooking something... that was the best I could do quickly without burning my food ;) – Jason Down May 30 '11 at 01:49
  • definitely don't want you to burn your food. :-) Thanks for updating, your answer is much better now. – Samuel Neff May 30 '11 at 02:28
  • I wish I could pick both of your answers! The slickness of this option appeals to me - this looks very clean, and is quite understandable. Thanks for the tip! =) – rybosome May 30 '11 at 02:47
  • @Ryan: No worries... just here to help when I can. @Samuel Neff: Food didn't burn... and went down quite well last night. – Jason Down May 30 '11 at 12:57
6

You definitely don't need a separate delegate for each. You can use Action delegates and lambda expressions to simplify it, like this:

private void SomeOtherMethod()
{
    Action action = () => pictureBox1.Image = Image.FromFile("example.png");
    if (pictureBox1.InvokeRequired)
    {
        Invoke(action);
    }
    else
    {
        action();
    }
}

Or you can separate out the if statement and InvokeRequired check and generalize it even more, like this:

public static void InvokeIfRequired(Control control, Action action)
{
    if (control.InvokeRequired)
    {
        control.Invoke(action);
    }
    else
    {
        action();
    }
}

private void SomeOtherMethod()
{
    InvokeIfRequired(() => pictureBox1.Image = Image.FromFile("example.png");
}
Samuel Neff
  • 73,278
  • 17
  • 138
  • 182
  • That's interesting...is that typically what's done in "enterprise"? Also, out of curiosity...what would happen if you used Invoke when it wasn't required? Would you just be making unnecessary method calls and increasing overhead, or does that violate thread-safety? – rybosome May 30 '11 at 01:55
  • I used to have a method exactly like this in my misc library (same name too), but I had it as an extension method. Came in handy. Though I prefer the way I did it below these days. Still, +1. – Jason Down May 30 '11 at 02:04
  • @Ryan, when you call `Invoke` the delegate is place on the controls message queue and then the caller waits until it is executed. You can do the same thing without waiting by calling `BeginInvoke`, which is probably better in most cases. Calling `Invoke` unnecessarily can cause the actions to be delayed since they are added to the message queue instead of processed immediately. It's probably not noticeable, but still unnecessary overhead. – Samuel Neff May 30 '11 at 02:27
1

I would use the MethodInvoker type in conjunction with an anonymous method or lambda expression. I would also build the invocation logic into the method itself, rather than using a separate thread-safe method:

void SomeMethod(/* with whatever args */) {
    if (InvokeRequired)
        Invoke(new MethodInvoker(() => SomeMethod(/* args used to call method */)));
    else
        // the method body itself
}
Bradley Smith
  • 13,353
  • 4
  • 44
  • 57