- c - 在位数组中找到第一个零
- linux - Unix 显示有关匹配两种模式之一的文件的信息
- 正则表达式替换多个文件
- linux - 隐藏来自 xtrace 的命令
我刚刚回答了一个关于 Task
是否可以更新 UI 的问题。当我玩我的代码时,我意识到我在一些事情上不清楚。
如果我有一个带有一个控件 txtHello
的 Windows 窗体,我似乎可以从任务更新 UI,如果我立即在 Task.Run 上执行它
:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
Task.Run(() =>
{
txtHello.Text = "Hello";
});
}
}
但是,如果我 Thread.Sleep
即使 5 毫秒,也会抛出预期的 CrossThread
错误:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
Task.Run(() =>
{
Thread.Sleep(5);
txtHello.Text = "Hello"; //kaboom
});
}
}
我不确定为什么会这样。是否对运行时间极短的 Task
进行了某种优化?
最佳答案
您没有发布异常堆栈跟踪,但我希望它看起来像这样:
System.InvalidOperationException: Cross-thread operation not valid: Control 'textBox1' accessed from a thread other than the thread it was created on.
at System.Windows.Forms.Control.get_Handle()
at System.Windows.Forms.Control.set_WindowText(String value)
at System.Windows.Forms.TextBoxBase.set_WindowText(String value)
at System.Windows.Forms.Control.set_Text(String value)
at System.Windows.Forms.TextBoxBase.set_Text(String value)
at System.Windows.Forms.TextBox.set_Text(String value)
at WindowsFormsApplicationcSharp2015.Form1.<.ctor>b__0_0() in D:\test\WindowsFormsApplicationcSharp2015\Form1.cs:line 27
我们可以看到异常是从Control.Handle
getter 属性中抛出的。事实上,如果我们看一下 source code对于该属性,正如预期的那样:
public IntPtr Handle {
get {
if (checkForIllegalCrossThreadCalls &&
!inCrossThreadSafeCall &&
InvokeRequired) {
throw new InvalidOperationException(SR.GetString(SR.IllegalCrossThreadCall,
Name));
}
if (!IsHandleCreated)
{
CreateHandle();
}
return HandleInternal;
}
}
有趣的部分是当我们查看调用 Control.Handle
的代码时。在这种情况下,这是 Control.WindowText setter 属性:
set {
if (value == null) value = "";
if (!WindowText.Equals(value)) {
if (IsHandleCreated) {
UnsafeNativeMethods.SetWindowText(new HandleRef(window, Handle), value);
}
else {
if (value.Length == 0) {
text = null;
}
else {
text = value;
}
}
}
}
请注意,只有当 IsHandleCreated
为 true
时,才会调用 Handle
属性。
为了完整起见,如果我们看一下 IsHandleCreated 的代码我们看到以下内容:
public bool IsHandleCreated {
get { return window.Handle != IntPtr.Zero; }
}
所以,你没有得到异常的原因是因为当 Task
执行时,窗口句柄还没有创建,这是自 >任务
在表单的构造函数中开始,也就是说,甚至在表单显示之前。
在创建窗口句柄之前,修改属性还不需要 UI 线程做任何工作。因此,在程序开始时的这个小时间窗口内,似乎可以从非 UI 线程调用控件实例上的方法,而不会出现“跨线程”异常。但很明显,这个特殊的小时间窗口的存在并没有改变这样一个事实,即我们应该始终确保从 UI 线程调用控制方法是安全的。
为了证明创建窗口句柄的时间是获得(或不获得)“跨线程”异常的决定因素这一点,请尝试修改您的示例以在开始任务之前强制创建窗口句柄,并注意你现在如何始终如一地获得预期的异常,即使没有 sleep :
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
// Force creation of window handle
var dummy = txtHello.Handle;
Task.Run(() =>
{
txtHello.Text = "Hello"; // kaboom
});
}
}
相关文档:Control.Handle
If the handle has not yet been created, referencing this property will force the handle to be created.
关于c# - 后台任务有时能够更新 UI?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31886711/
我正在使用 Java 编写一个时钟程序,该程序能够“滴答作响”,但它存在问题。我认为它与 getter 和 setter 或 toString() 方法有关。 计数器类 package clock;
const Index = () => { // Ref Links const frefLinks = { 1: useRef(1), 2: useRef(2), 3: useRef(3
所以我读了here不能 pickle 装饰函数。确实: import multiprocessing as mp def deco(f): def wrapper(*args, **kwarg
我在go1.11.2 linux/amd64 版本。当包godog使用 go get github.com/DATA-DOG/godog/ 安装,godog 可执行文件在 $GOPATH/bin/中创
如何正确压缩字符串,以便 PHP 能够解压缩? 我试过这个: public static byte[] compress(String string) throws IOException {
我们这里的问题是表明 在测试中使用 Kleene 代数。 在 b 的值由 p 保留的情况下,我们有交换条件 bp = pb;两个程序之间的等价性简化为等式 在 b 的值不被 p 保留的情况下,我们有交
我有一个与我的网络相关的非常奇怪的问题,我在具有多个接口(interface)的 VirtualBox 上安装了 RDO Grizzly OpenStack。 虚拟盒子: eth0 - managem
我正在尝试使用 Passport.js授权谷歌OAuth2在 Node.js .我整个星期都在尝试让它工作,但不知道为什么它不工作,所以现在我求助于 stack 寻求一些潜在的帮助。我已经尝试了所有在
我是一名优秀的程序员,十分优秀!