【问题标题】:How to get return value when BeginInvoke/Invoke is called in C#在 C# 中调用 BeginInvoke/Invoke 时如何获取返回值
【发布时间】:2010-02-06 17:44:49
【问题描述】:

我有这个应该是线程安全的小方法。一切正常,直到我希望它具有返回值而不是 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;
        }

    }

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

【问题讨论】:

  • 如果您需要使用从调用返回的值,那一定是因为您需要“继续传递样式”模式。这可以通过asyncawaitTask 来缓解。

标签: c# .net multithreading invoke begininvoke


【解决方案1】:

您必须调用 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;
  }
}

【讨论】:

    【解决方案2】:

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

    【讨论】:

    • 总体上很好的例子,虽然在我的情况下 nobugz 解决方案是完美的,正是需要的。
    【解决方案3】:
    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;
    }
    

    【讨论】:

    • 看起来类似于 nobugz 解决方案,但他看起来更干净 :-)
    • 是的,我同意。当我发布我的解决方案时,我还没有看到 nobugz 解决方案:)
    【解决方案4】:

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

    【讨论】:

    • 可以是 Invoke 或 BeginInvoke,只要我能取回值。我只需要从其他线程中读取组合框或文本框的值。
    【解决方案5】:

    这是你想要的吗?

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

    【讨论】:

    • 不完全。我想 BeginInvoke 的使用不是必需的,因为我只想读取 ComboBox 或 TextBox 的值,并且该值对于执行其他命令很重要。所以我可以在 Invoke 中使用它。只是不确定在我的情况下如何使用您的示例,它的主要目的是从另一个线程调用方法,读取 gui 值并返回给我,以便程序可以走得更远。
    【解决方案6】:
    delegate string StringInvoker();
        string GetControlText()
        {
            if (control.InvokeRequired)
            {
                string controltext = (string)control.Invoke(new StringInvoker(GetControlText));
                return(controltext);
            }
            else
            {
                return(control.Text);
            }
        }
    

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

    【讨论】:

      【解决方案7】:

      这里有更清洁的解决方案

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-09-17
        • 1970-01-01
        相关资源
        最近更新 更多