跨线程操作Form时,必须在当前线程创建(包括Form.Show),还是 Form.Show 的时候必须在当前线程?

oldhunter 2019-03-14 03:25:57
跨线程操作Form时,必须在当前线程创建(包括Form.Show),还是 Form.Show 的时候必须在当前线程?

之前的作法是创建Form对象,没有使用“工作线程”,但显示窗体(Form.Show)以及后续对Form的操作,都是使用的“工作线程”,即通过 Control.Invoke 的方式进行显示与后续操作。

也就是说 Form form = new Form() 没有在“工作线程”,但 form.Show 及其它操作在“工作线程”。

这样做之前没有问题,但最近发现程序有些异常,界面UI经常无故卡住,感觉可能跟这个有关系。想问一下,准确规范是什么样的?

Form form = new Form(),有没有必要在“工作线程”中创建? 不考虑 CheckForIllegalCrossThreadCalls = false 的情况。
...全文
429 14 打赏 收藏 转发到动态 举报
AI 作业
写回复
用AI写文章
14 条回复
切换为时间正序
请发表友善的回复…
发表回复
xiaoyety 2019-03-16
  • 打赏
  • 举报
回复
wanghui0380 2019-03-14
  • 打赏
  • 举报
回复
  //方法一:当前线程调度器
Task.Factory.StartNew(() =>
{
this.label1.Text = "方法一";
}, CancellationToken.None, TaskCreationOptions.None, TaskScheduler.FromCurrentSynchronizationContext());

//方法二:利用await进行上下文切换;
this.label1.Text= await Task<string>.Run(() => "方法二"); //把复杂计算丢到default,await会进行上下文切换;

//方法三:手动显式切换上下文
Task<string>.Run(() => "方法三").ContinueWith(p => { this.label1.Text = p.Result; },
/ TaskScheduler.FromCurrentSynchronizationContext());

//方法四:直接跨越使用上下文同步
SynchronizationContext uiContext=SynchronizationContext.Current;
Task.Run(() =>
{
uiContext.Post(state =>
{
this.label1.Text = "方法四";
},null);
});

//方法五:invoke,begininvoke 这个我就不写了,楼上说的很多了
  • 打赏
  • 举报
回复
引用 6 楼 oldhunter 的回复:
之前不是这样写的,为什么程序不报错? 不是应该每次都要报错的吗? 我也没有设置 CheckForIllegalCrossThreadCalls = false
new Form() 不报错,但是后续就乱了——报错了。
  • 打赏
  • 举报
回复
引用 1 楼 oldhunter 的回复:
因为之前的理解是,form.Show 后,WINDOWS的消息泵才开始工作,所以理解为 Form form = new Form() 在哪个线程都可以
我是无法理解这个推理的。
  • 打赏
  • 举报
回复
引用 楼主 oldhunter 的回复:
跨线程操作Form时,必须在当前线程创建(包括Form.Show),还是 Form.Show 的时候必须在当前线程? 之前的作法是创建Form对象,没有使用“工作线程”,但显示窗体(Form.Show)以及后续对Form的操作,都是使用的“工作线程”,即通过 Control.Invoke 的方式进行显示与后续操作。 也就是说 Form form = new Form() 没有在“工作线程”,但 form.Show 及其它操作在“工作线程”。
其实并非“当前线程”,而是对控件的操作应该用创建控件的那个线程。说“当前”其实是容易歧义的,因为你这个“当前”飘忽不定! 所以,如果你把正你 new Form() 的时候使用之前创建的 UI 线程来创建它,那么参照点就一直是主窗体线程,没有问题。而如果你 new Form() 的时候根本就是飘忽不定的,那么后边很可能越来越乱。
wanghui0380 2019-03-14
  • 打赏
  • 举报
回复
线程有上下文环境。

要更新UI的线程就得放在UI环境,所以之前能,现在不能。只是因为之前是在UI的Context上

有关这块,请看TaskScheduler线程调度器的使用。

Task.Factory.StartNew 默认在TaskScheduler.Default 线程池里,而非在当前上下文上
zhuowp 2019-03-14
  • 打赏
  • 举报
回复
引用 7 楼 oldhunter 的回复:
[quote=引用 2 楼 zhuowp 的回复:]
都需要。凡是和ui相关的操作,都需要放到ui线程里执行。

在winform里面,可以通过 ui控件.Invoke(),将ui操作的东西委托给ui线程。


之前不是这样写的,为什么程序不报错? 不是应该每次都要报错的吗? 我也没有设置 CheckForIllegalCrossThreadCalls = false
[/quote]要跨线程操作才需要这样。比如你用Task.Factory.StartNew(() =>{})或者 new Thread()这样的。

如果你的调用线程本身就是ui线程,比如在鼠标点击事件中去操作,就不需要。
oldhunter 2019-03-14
  • 打赏
  • 举报
回复
引用 2 楼 zhuowp 的回复:
都需要。凡是和ui相关的操作,都需要放到ui线程里执行。

在winform里面,可以通过 ui控件.Invoke(),将ui操作的东西委托给ui线程。


之前不是这样写的,为什么程序不报错? 不是应该每次都要报错的吗? 我也没有设置 CheckForIllegalCrossThreadCalls = false
oldhunter 2019-03-14
  • 打赏
  • 举报
回复
之前不是这样写的,为什么程序不报错? 不是应该每次都要报错的吗? 我也没有设置 CheckForIllegalCrossThreadCalls = false
zhuowp 2019-03-14
  • 打赏
  • 举报
回复
引用 4 楼 oldhunter 的回复:
[quote=引用 3 楼 zhuowp 的回复:]
甚至,你的某个ui控件绑定了后台数据的某个属性,通过改变这个属性会引起ui的变化,那么给这个属性赋值的时候也要在ui线程中执行。


另外,如果不是设置某个属性,只是读取一下属性,也要用 Invoke 吗?[/quote]如果你读取的是控件的属性,比如Width,Height这些的,需要。
oldhunter 2019-03-14
  • 打赏
  • 举报
回复
引用 3 楼 zhuowp 的回复:
甚至,你的某个ui控件绑定了后台数据的某个属性,通过改变这个属性会引起ui的变化,那么给这个属性赋值的时候也要在ui线程中执行。


另外,如果不是设置某个属性,只是读取一下属性,也要用 Invoke 吗?
zhuowp 2019-03-14
  • 打赏
  • 举报
回复
甚至,你的某个ui控件绑定了后台数据的某个属性,通过改变这个属性会引起ui的变化,那么给这个属性赋值的时候也要在ui线程中执行。
zhuowp 2019-03-14
  • 打赏
  • 举报
回复
都需要。凡是和ui相关的操作,都需要放到ui线程里执行。

在winform里面,可以通过 ui控件.Invoke(),将ui操作的东西委托给ui线程。
oldhunter 2019-03-14
  • 打赏
  • 举报
回复
因为最近有一个错误提示:在某个线程上创建的控件,不能成为在另一个线程上创建的控件的父级

控件的创建,如何理解? Form form = new Form() 算是创建,还是 form.Show() 算是创建?

因为之前的理解是,form.Show 后,WINDOWS的消息泵才开始工作,所以理解为 Form form = new Form() 在哪个线程都可以,只有
form.Show() 的时候,才算是确定线程。

111,094

社区成员

发帖
与我相关
我的任务
社区描述
.NET技术 C#
社区管理员
  • C#
  • AIGC Browser
  • by_封爱
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

让您成为最强悍的C#开发者

试试用AI创作助手写篇文章吧