我有一个场景.(Windows Forms,C#,.NET)
UserControl_Load方法,则UI在加载方法执行的持续时间内变得无响应.伪代码看起来像这样:
代码1
UserContrl1_LoadDataMethod()
{
if (textbox1.text == "MyName") // This gives exception
{
//Load data corresponding to "MyName".
//Populate a globale variable List<string> which will be binded to grid at some later stage.
}
}
Run Code Online (Sandbox Code Playgroud)
它给出的例外是
跨线程操作无效:从创建它的线程以外的线程访问控件.
为了更多地了解这一点,我做了一些谷歌搜索,并提出了一个建议,如使用下面的代码
代码2
UserContrl1_LoadDataMethod()
{
if (InvokeRequired) // Line #1
{
this.Invoke(new MethodInvoker(UserContrl1_LoadDataMethod));
return;
}
if (textbox1.text == "MyName") // Now it wont give an exception
{
//Load data correspondin to "MyName"
//Populate a globale …Run Code Online (Sandbox Code Playgroud) 在我维护一个严重违反winforms中的跨线程更新规则的旧应用程序的过程中,我创建了以下扩展方法,以便在我发现它们时快速修复非法调用:
/// <summary>
/// Execute a method on the control's owning thread.
/// </summary>
/// <param name="uiElement">The control that is being updated.</param>
/// <param name="updater">The method that updates uiElement.</param>
/// <param name="forceSynchronous">True to force synchronous execution of
/// updater. False to allow asynchronous execution if the call is marshalled
/// from a non-GUI thread. If the method is called on the GUI thread,
/// execution is always synchronous.</param>
public static void SafeInvoke(this Control uiElement, Action updater, bool forceSynchronous)
{
if …Run Code Online (Sandbox Code Playgroud) 我有一个带有自定义控件的表单.
我的表格中有一个方法:
private void SetEnabledOnControls(bool val)
{
if (InvokeRequired)
{
Invoke((Action<bool>)SetEnabledOnControls, val);
}
else
{
//do the work - iterate over child controls,
//and they iterate over their children, etc...
}
}
Run Code Online (Sandbox Code Playgroud)
在else分支上的方法内部,我得到了提到的异常:
Cross-thread operation not valid: Control 'txtNumber' accessed from a thread other than the thread it was created on.
我的场景实际上有点复杂 - 我只是将其推断为一个例子.实际上发生的是我正在使用WorkflowFoundation - 我在WorkflowApplication中运行StateMachineActivity(CTP1)(它在自己的线程中运行),我订阅了它的事件,并从那里调用SetEnabledOnControls.此外,我正在使用书签来恢复我的工作流程(而且,旁边有MEF,没有涉及场景).
所有这些都与我对InvokeRequired的明显误解无关 - 如果InvokeRequired为false,我有可能有交叉线程异常吗?我没有"手动"创建任何控件 - 它都在设计者放置的Initialize()中.
任何人都可以对此有所了解吗?
谢谢!
编辑
使用GWLlosa建议,我使用了跟踪ThreadId System.Threading.Thread.CurrentThread.ManagedThreadId.现在来了奇怪的部分...... Initialize()中的线程id是10.在传递前2个状态之间,它带有Id 13 - InvokeRequired为true,并且它被正确调用.但是,在第二个状态之后,当它SetEnabledOnControls再次进入13时,但这次InvokeRequired是假的!怎么会!?当然,后来它无法改变儿童控制(这并不奇怪).可能是表格以某种方式改变了它所生活的线程?
编辑2 现在我打电话给:
if …Run Code Online (Sandbox Code Playgroud) 尝试更新图像时出现此错误.这是一个跨线程更新,但我使用.Invoke(),所以这不应该是问题,如果它.
这是我的情况,有2个类和我的主要表单Form1:
Class1:有一个方法doSomethingAndCall(callback)创建一个新线程Class2:具有动态创建的控件,带有一个触发Class1.doSomethingAndCall(newCallback)的按钮
在代码中它看起来像这样(它从Class2.Button_Click开始):
Class Class1
public shared sub doSomethingAndCallAsync(state as object)
Console.WriteLine(Form1.InvokeRequired) 'output: false
Console.WriteLine(Form1.IsHandleCreated) 'output: false
Form1.Invoke(state.callback) 'throws System.InvalidOperationException
end sub
public shared sub doSomethingAndCall(callback as object)
System.Threading.ThreadPool.QueueUserWorkItem(AddressOf doSomethingAndCallAsync, New With {.callback = callback})
end sub
End Class
Class Class2
Public Delegate Sub doSomethingDelegate()
Public Sub doSomething()
Console.WriteLine("success!")
End Sub
Public Sub Button_Click(ByVal sender As Object, ByVal e As System.EventArgs)
Class1.doSomethingAndCall(New doSomethingDelegate(AddressOf doSomething))
End Sub
End Class
Run Code Online (Sandbox Code Playgroud)
我得到的确切例外是:
在创建窗口句柄之前,无法在控件上调用Invoke或BeginInvoke
并且我可以看到第4行中的console.WriteLine向我显示该表单实际上并未创建.所以我添加了这个处理程序,现在它真的很混乱:
Private Sub Form1_HandleCreated(sender As Object, e As System.EventArgs) Handles …Run Code Online (Sandbox Code Playgroud) 在执行我的win表单应用程序时,我将获得一个跨线程异常似乎是一个随机的机会.以下是我试图管理它的方法:
private void ToOutput(string s)
{
if (!this.IsHandleCreated)
this.CreateHandle();
if (FormOutputArea.InvokeRequired)
{
FormOutputArea.Invoke(new Action(delegate ()
{
FormOutputArea.AppendText(s + Environment.NewLine);
}));
}
else
{
FormOutputArea.AppendText(s + Environment.NewLine);
}
}
Run Code Online (Sandbox Code Playgroud)
它似乎InvokeRequired并不总是准确的.我尝试BeginInvoke了相同的结果.
编辑:即使我检查IsHandleCreated并InvokeRequired使用断点,它们也设置为true,但else条件的分支仍然执行.
这是一个截图,显示现在抛出异常的位置:
