48

我有这个应该是线程安全的小方法。一切正常,直到我希望它具有返回值而不是 void。调用 BeginInvoke 时如何获取返回值?

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

    }

编辑:我想在这种情况下不需要 BeginInvoke,因为在线程可以继续之前我需要来自 GUI 的值。所以使用 Invoke 也很好。只是不知道如何在以下示例中使用它来返回值。

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;
        }

    }

但也不确定如何使用该代码获得价值;)

4

7 回答 7

76

您必须 Invoke() 才能等待函数返回并获取其返回值。您还需要另一种委托类型。这应该有效:

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;
  }
}
于 2010-02-06T18:09:39.663 回答
20

EndInvoke可用于从BeginInvoke调用中获取返回值。例如:

    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);
    }
}
于 2010-02-06T18:23:07.970 回答
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;
}
于 2010-02-06T18:19:05.810 回答
2

如果你想从你的方法返回值,你不应该使用异步版本的方法,你应该使用.Invoke(...). 这是同步的,即它将执行您的委托,并且在完成之前不会返回。在您现在的示例中,BeginInvoke 将发送执行您的委托的请求,并立即返回。所以没有什么可以退货的。

于 2010-02-06T17:48:08.557 回答
2

这是你想要的吗?

// 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);
于 2010-02-06T17:48:35.437 回答
0
delegate string StringInvoker();
    string GetControlText()
    {
        if (control.InvokeRequired)
        {
            string controltext = (string)control.Invoke(new StringInvoker(GetControlText));
            return(controltext);
        }
        else
        {
            return(control.Text);
        }
    }

//简单优雅但需要等待另一个线程执行委托;但是,如果没有结果就无法继续...

于 2014-01-21T11:48:13.030 回答
0

这是更清洁的解决方案

    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);
    }

用法:

    public string Name
    {
        get => txtName.GetThreadSafeText();
        set => txtName.SetThreadSafeText(value);
    }
于 2021-09-10T06:53:16.077 回答