简体   繁体   English

C#Func Delegate引发线程异常

[英]C# Func Delegate throws Thread Exception

So, maybe I misunderstood the usage of Func but 所以,也许我误解了Func的用法,但是

Func<ComboBox, string> getCurrentValue = s => s.SelectedValue.ToString();

Creates a Thread Error when calling getCurrentValue(cb_message_type) from my Workerthread. 从我的Workerthread调用getCurrentValue(cb_message_type)时创建线程错误。 Whats the best solution to get the Selected Value of the Combobox? 什么是获得组合框选定值的最佳解决方案?

Many thanks, 非常感谢,
rAyt A

Edit 编辑
MSDN MSDN

"The underlying type of a lambda expression is one of the generic Func delegates. This makes it possible to pass a lambda expression as a parameter without explicitly assigning it to a delegate." “ lambda表达式的基础类型是泛型Func委托之一。这使得可以将lambda表达式作为参数传递而无需显式将其分配给委托。”

Since windows controls have thread affinity, you have 2 options: 由于Windows控件具有线程关联性,因此您有2个选项:

  1. query this data before doing your threading code, for example passing it in as the state to the worker 在执行线程代码之前查询此数据,例如将其作为状态传递给工作程序
  2. query it in the worker via Control.Invoke 通过Control.Invoke在工作程序中查询它

Since the first is trivial, I'll give an example of the second using captured variables: 由于第一个很简单,因此我将举一个使用捕获变量的第二个示例:

object value = null;
yourCombo.Invoke((MethodInvoker) delegate {value=yourCombo.SelectedValue;});
string s = value.ToString();

Here the bits inside delegate {...} happen on the UI thread, even if the code around it is on the worker thread. 这里, delegate {...}的位发生在UI线程上,即使它周围的代码在工作线程上也是如此。 You can mix the above either inside your func, or call the entire func once you've switched threads. 您可以函数内部混合以上内容,也可以切换线程后调用整个函数。

You need to call Control.Invoke with that delegate - or make the delegate itself call it. 您需要使用该委托调用Control.Invoke或让委托本身调用它。 Using a lambda expression doesn't change the threading requirements of Windows Forms - it just makes it easier to create a delegate. 使用lambda表达式不会更改Windows Forms的线程要求,而只是使创建委托变得更加容易。

You might want to make a convenience method to do this: 您可能想使用一种便捷的方法来执行此操作:

// (Untested)
public static Func<TControl, TResult> WrapInvocation(Func<TControl,TResult> func)
    where TControl : Control
{
    return control => {
        return (TResult) control.Invoke(func);
    };
}

Use as: 用于:

Func<ComboBox, string> getCurrentValue = s => s.SelectedValue.ToString();
getCurrentValue = WrapInvocation(getCurrentValue);

Then you can call getCurrentValue(comboBox) from any thread. 然后,您可以从任何线程调用getCurrentValue(comboBox)

The problem is that UI controls can only be used on the UI thread, 问题是UI控件只能在UI线程上使用,

You need to call the Invoke method within the other thread, like this: 您需要在另一个线程中Invoke方法,如下所示:

Func<ComboBox, string> getCurrentValue =
    s => s.Invoke(new Func<object>(() => s.SelectedValue)).ToString();

The Invoke method takes a delegate and executes it on the UI thread. Invoke方法接受一个委托,并在UI线程上执行它。

Generally speaking, you cannot access UI controls from a thread other than that they were created on. 通常,您不能从创建线程的线程之外的线程访问UI控件。 To overcome that, you'll either have to check ISynchronizeInvoke.InvokeRequired on the control in question and branch, invoke a delegate, etc. or use a SynchronizationContext . 为了克服这个问题,您要么必须在有问题和分支的控件上检查ISynchronizeInvoke.InvokeRequired ,要么调用委托等,或者使用SynchronizationContext First option is very cumbersome, whereas second one is pretty elegant: 第一个选择非常麻烦,而第二个选择则非常优雅:

var synchronizationContext = WindowsFormsSynchronizationContext.Current;
string currentValue = "";    

synchronizationContext.Send(
    () => currentValue = getCurrentValue(comboBox), 
    null);

如果线程将仅读取ComboBox,则可行的最佳选择是在线程上有一个事件处理程序,该事件处理程序在更改时随时获取ComboBox值,然后通过可从任何线程读取的属性公开该值。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM