53

I've this little method which is supposed to be thread safe. Everything works till i want it to have return value instead of void. How do i get the return value when BeginInvoke is called?

public static string readControlText(Control varControl) {
        if (varControl.InvokeRequired) {
            varControl.BeginInvoke(new MethodInvoker(() => readControlText(varControl)));
        } else {
            string varText = varControl.Text;
             return varText;
        }

    }

Edit: I guess having BeginInvoke is not nessecary in this case as i need value from GUI before the thread can continue. So using Invoke is good as well. Just no clue how to use it in following example to return value.

private delegate string ControlTextRead(Control varControl);
    public static string readControlText(Control varControl) {
        if (varControl.InvokeRequired) {
            varControl.Invoke(new ControlTextRead(readControlText), new object[] {varControl});
        } else {
            string varText = varControl.Text;
             return varText;
        }

    }

But not sure how to get value using that code either ;)

1
  • If you need to work with the value returned from an invoke, that must be because you need a "continuation passing style" pattern. Which can be alleviated with async, await and Task.
    – v.oddou
    Commented Sep 23, 2016 at 1:44

7 Answers 7

84

You have to Invoke() so you can wait for the function to return and obtain its return value. You'll also need another delegate type. This ought to work:

public static string readControlText(Control varControl) {
  if (varControl.InvokeRequired) {
    return (string)varControl.Invoke(
      new Func<String>(() => readControlText(varControl))
    );
  }
  else {
    string varText = varControl.Text;
    return varText;
  }
}
0
20

EndInvoke may be used to get a return value from a BeginInvoke call. For example:

    public static void Main() 
    {
        // The asynchronous method puts the thread id here.
        int threadId;

        // Create an instance of the test class.
        AsyncDemo ad = new AsyncDemo();

        // Create the delegate.
        AsyncMethodCaller caller = new AsyncMethodCaller(ad.TestMethod);

        // Initiate the asychronous call.
        IAsyncResult result = caller.BeginInvoke(3000, 
            out threadId, null, null);

        Thread.Sleep(0);
        Console.WriteLine("Main thread {0} does some work.",
            Thread.CurrentThread.ManagedThreadId);

        // Call EndInvoke to wait for the asynchronous call to complete,
        // and to retrieve the results.
        string returnValue = caller.EndInvoke(out threadId, result);

        Console.WriteLine("The call executed on thread {0}, with return value \"{1}\".",
            threadId, returnValue);
    }
}
1
  • Overall good example, although in my case nobugz solution is perfect and exactly what was required.
    – MadBoy
    Commented Feb 6, 2010 at 18:30
4
public static string readControlText(Control varControl)
{
    if (varControl.InvokeRequired)
    {
        string res = "";
        var action = new Action<Control>(c => res = c.Text);
        varControl.Invoke(action, varControl);
        return res;
    }
    string varText = varControl.Text;
    return varText;
}
2
  • Looks similar to nobugz solution but his looks cleaner to me :-)
    – MadBoy
    Commented Feb 6, 2010 at 18:23
  • Yep, I agree. When I posted my solution, I don't see nobugz solution yet:) Commented Feb 6, 2010 at 18:25
1

If you want a return value from you method, you shouldn't be using async version of the method, you should use .Invoke(...). Which is synchronous, that is it will execute your delegate, and won't return until it's complete. In your example as it is now, BeginInvoke will send the request to execute your delegate, and return right away. So there is nothing to return.

1
  • It can be Invoke or BeginInvoke as long as i get back value. All i need is to read value of combobox or textbox from other thread.
    – MadBoy
    Commented Feb 6, 2010 at 17:54
1

Is something like this what you wanted?

// begin execution asynchronously
IAsyncResult result = myObject.BeginInvoke("data.dat", null, null);

// wait for it to complete
while (result.IsCompleted == false) {
   // do some work
   Thread.Sleep(10);
   }

// get the return value
int returnValue = myObject.EndInvoke(result);
1
  • Not quite. I guess the use of BeginInvoke is not nessecary since i just want to read value of ComboBox or TextBox and that value is important to execute other commands. So i could use it in Invoke. Just not sure how i could use your example in my case where the main purpose of it is to call method from another thread, read gui value and get back to me so program can go further.
    – MadBoy
    Commented Feb 6, 2010 at 17:57
0
delegate string StringInvoker();
    string GetControlText()
    {
        if (control.InvokeRequired)
        {
            string controltext = (string)control.Invoke(new StringInvoker(GetControlText));
            return(controltext);
        }
        else
        {
            return(control.Text);
        }
    }

//simple & elegant but it is needed to wait for another thread to execute delegate; however if you cannot proceed without the results...

0

Here is more cleaner solution

    public delegate void InvokeIfRequiredDelegate<T>(T obj) where T : ISynchronizeInvoke;
    public static void InvokeIfRequired<T>(this T obj, InvokeIfRequiredDelegate<T> action) where T : ISynchronizeInvoke
    {
        if (obj.InvokeRequired)
            obj.Invoke(action, new object[] { obj });
        else
            action(obj);
    }

    public static string GetThreadSafeText(this Control control)
    {
        string value = string.Empty;
        control.InvokeIfRequired((c) => value = c.Text);
        return value;
    }
    public static void SetThreadSafeText(this Control control, string value)
    {
        control.InvokeIfRequired((c) => c.Text = value);
    }

Usage:

    public string Name
    {
        get => txtName.GetThreadSafeText();
        set => txtName.SetThreadSafeText(value);
    }

Not the answer you're looking for? Browse other questions tagged or ask your own question.