关于Task

sj490790083 2018-04-06 10:40:46
有个疑问,在ThreadPool.QueueUserWorkItem的委托中去更新UI会报跨线程调用异常,这个没问题,可以理解。
但是如果在Task的委托中去做同样的操作,就不会报异常,但是界面也不会更新,除非UI接收到重绘消息,则会更新。

为什么?Task不也是基于线程池吗?同样是在非UI线程修改UI,为啥一个报错一个不报错。谢谢
...全文
340 17 打赏 收藏 转发到动态 举报
写回复
用AI写文章
17 条回复
切换为时间正序
请发表友善的回复…
发表回复
qq_26671507 2018-04-08
  • 打赏
  • 举报
回复
Task没问题,会更新text的值,你那为啥不更新我就不清楚了。
  • 打赏
  • 举报
回复
贴出你的代码来。什么叫做“Task的委托"?Task有多个方法,谁知道你说的是什么方法?
xuzuning 2018-04-07
  • 打赏
  • 举报
回复
在Task的委托中去做同样的操作,就不会报异常 表示 Task 是线程安全的,如果你假设 Task 里有 Lock,那么就没有疑问了吧
sj490790083 2018-04-07
  • 打赏
  • 举报
回复
引用 14 楼 sp1234 的回复:
所以你要先检查你的各种设置,从所谓的逻辑的 CheckForIllegalCrossThreadCalls 设置,到你的 Program 类中有没有什么特殊设置,到你创建窗体是不是在主 UI 线程创建的,到你的 vs 开发环境设置。如果不明白为什么会乱,就不要继续编程。停下一切工作,先把 bug 为什么会被忽略,这个问题搞清楚。
感谢!
sj490790083 2018-04-07
  • 打赏
  • 举报
回复
引用 14 楼 sp1234 的回复:
所以你要先检查你的各种设置,从所谓的逻辑的 CheckForIllegalCrossThreadCalls 设置,到你的 Program 类中有没有什么特殊设置,到你创建窗体是不是在主 UI 线程创建的,到你的 vs 开发环境设置。如果不明白为什么会乱,就不要继续编程。停下一切工作,先把 bug 为什么会被忽略,这个问题搞清楚。
Task内部的异常会被包装到Task.AggregateException中,如果不去访问Task.Result,好像是不会抛出异常,VS的设置好像并没问题
  • 打赏
  • 举报
回复
既然程序都乱了,怎么会在 DEBUG 下没有被 vs 捕获、并且直接跳入调试状态呢? 开发时,就是要尽早地让 bug 跳出来,用 vs 调试器修改程序。如果连异常都不能捕获到了,那么开发就进行不下去了。我如果遇到一个开发环境不能捕获异常,我就改行了。
  • 打赏
  • 举报
回复
所以你要先检查你的各种设置,从所谓的逻辑的 CheckForIllegalCrossThreadCalls 设置,到你的 Program 类中有没有什么特殊设置,到你创建窗体是不是在主 UI 线程创建的,到你的 vs 开发环境设置。如果不明白为什么会乱,就不要继续编程。停下一切工作,先把 bug 为什么会被忽略,这个问题搞清楚。
sj490790083 2018-04-07
  • 打赏
  • 举报
回复
引用 9 楼 sp1234 的回复:
正常情况下会报
System.InvalidOperationException:“线程间操作无效: 从不是创建控件“btnResult”的线程访问它。”
你调试的过程是什么?指定断点或者单步调试了吗?看到并且断言了了 result 输出结果是否正确了吗? 贴出来实际测试结果截图。
加了个断点,其实是报错了,最后一句Return没执行,所以访问t1.Result的时候出错了,但是Btn的Text还是成功更新了
sj490790083 2018-04-07
  • 打赏
  • 举报
回复
引用 9 楼 sp1234 的回复:
正常情况下会报
System.InvalidOperationException:“线程间操作无效: 从不是创建控件“btnResult”的线程访问它。”
你调试的过程是什么?指定断点或者单步调试了吗?看到并且断言了了 result 输出结果是否正确了吗? 贴出来实际测试结果截图。
ThreadPool.QueueUserWorkItem((obj) => this.button1.Text = "ss");这句执行不用Invoke的话确实会报错。可以理解。
  • 打赏
  • 举报
回复
正常情况下会报
System.InvalidOperationException:“线程间操作无效: 从不是创建控件“btnResult”的线程访问它。”
你调试的过程是什么?指定断点或者单步调试了吗?看到并且断言了了 result 输出结果是否正确了吗? 贴出来实际测试结果截图。
sj490790083 2018-04-07
  • 打赏
  • 举报
回复
引用 6 楼 sj490790083 的回复:
[quote=引用 5 楼 sp1234 的回复:] [quote=引用 3 楼 sj490790083 的回复:]

    //Task
     Task<int> t1 = new Task<int>(() =>
            {
                int result = 3;
                this.btnResult.Text = result.ToString();
                for (; result <= 10; result++)
                    result++;
                return result;[code=csharp]
}); t1.Start(); 。。。。。。。。。 [/code]
你的这个方法是在哪里调用的?根本看不出来嘛! 比如说你放在窗体的 Button_Click 事件处理中调用它,不会抛出异常吗?[/quote] 不会 ,我在界面Load事件中写的只是好奇,没有报错。然后鼠标滑过Button,触发Button重绘,界面才会更新。[/quote] 但是在ThreadPool.QueueUserWorkItem中去写的话,不出意外会报错,可能Task内部有什么机制吧,如楼上所说线程安全,但是不知道原理是啥
sj490790083 2018-04-07
  • 打赏
  • 举报
回复
引用 5 楼 sp1234 的回复:
[quote=引用 3 楼 sj490790083 的回复:]

    //Task
     Task<int> t1 = new Task<int>(() =>
            {
                int result = 3;
                this.btnResult.Text = result.ToString();
                for (; result <= 10; result++)
                    result++;
                return result;[code=csharp]
}); t1.Start(); 。。。。。。。。。 [/code]
你的这个方法是在哪里调用的?根本看不出来嘛! 比如说你放在窗体的 Button_Click 事件处理中调用它,不会抛出异常吗?[/quote] 不会 ,我在界面Load事件中写的只是好奇,没有报错。然后鼠标滑过Button,触发Button重绘,界面才会更新。
sj490790083 2018-04-07
  • 打赏
  • 举报
回复
引用 7 楼 sp1234 的回复:
这个代码如果放在正常的测试、调试中,就回报
System.InvalidOperationException:“线程间操作无效: 从不是创建控件“textBox1”的线程访问它。”
你应该一次性地贴出准确地截图、或者源代码来,但凡动手很迅速的人贴图是也总是能一步到位说明自己的问题环境。不要像多么舍不得把自己的代码公开似地。
  private void Form1_Load(object sender, EventArgs e)
        {
            //Task.Factory.StartNew(() =>
            //{
            //    Thread.Sleep(2000);
            //    //this.button1.Text="ss";
            //}).ContinueWith(t => this.button1.Text = "11", TaskScheduler.FromCurrentSynchronizationContext());

            Task<int> t1 = new Task<int>(() =>
            {
                int result = 3;
                this.button1.Text = result.ToString();
                for (; result <= 10; result++)
                    result++;
                return result;
               
            });
            t1.Start();
            t1.ContinueWith(task => { Thread.Sleep(3000); this.button1.Text = task.Result.ToString(); },TaskScheduler.FromCurrentSynchronizationContext());

            //ThreadPool.QueueUserWorkItem((obj) => this.button1.Text = "ss");
        }
    }
就是很简单的测试程序,直接运行不会报错,并且界面也会更新,后面的ContinueWith会阻塞UI线程3秒。这个没问题,但是前面的代码也没报错直接成功更新了。
  • 打赏
  • 举报
回复
这个代码如果放在正常的测试、调试中,就回报
System.InvalidOperationException:“线程间操作无效: 从不是创建控件“textBox1”的线程访问它。”
你应该一次性地贴出准确地截图、或者源代码来,但凡动手很迅速的人贴图是也总是能一步到位说明自己的问题环境。不要像多么舍不得把自己的代码公开似地。
  • 打赏
  • 举报
回复
引用 3 楼 sj490790083 的回复:

    //Task
     Task<int> t1 = new Task<int>(() =>
            {
                int result = 3;
                this.btnResult.Text = result.ToString();
                for (; result <= 10; result++)
                    result++;
                return result;[code=csharp]
}); t1.Start(); 。。。。。。。。。 [/code]
你的这个方法是在哪里调用的?根本看不出来嘛! 比如说你放在窗体的 Button_Click 事件处理中调用它,不会抛出异常吗?
sj490790083 2018-04-07
  • 打赏
  • 举报
回复
引用 2 楼 sp1234 的回复:
贴出你的代码来。什么叫做“Task的委托"?Task有多个方法,谁知道你说的是什么方法?

//Task
     Task<int> t1 = new Task<int>(() =>
            {
                int result = 3;
                this.btnResult.Text = result.ToString();
                for (; result <= 10; result++)
                    result++;
                return result;

            });
            t1.Start();
           //ThreadPool
           ThreadPool.QueueUserWorkItem((obj) => this.btnResult.Text = "ss");
sj490790083 2018-04-07
  • 打赏
  • 举报
回复
引用 2 楼 sp1234 的回复:
贴出你的代码来。什么叫做“Task的委托"?Task有多个方法,谁知道你说的是什么方法?

    //Task
     Task<int> t1 = new Task<int>(() =>
            {
                int result = 3;
                this.btnResult.Text = result.ToString();
                for (; result <= 10; result++)
                    result++;
                return result;[code=csharp]
}); t1.Start(); //ThreadPool ThreadPool.QueueUserWorkItem((obj) => this.btnResult.Text = "ss"); [/code] 我知道如果用Task.ContinueWith方法的带TaskScheduler重载版本,传入TaskScheduler.FromCurrentSynchronizationContext()的话,ContinueWith中执行的操作会由UI线程去执行,不会报跨线程调用异常。但是上面的Task的构造函数中并没有带这种调度器的参数,为什么也不会报跨线程访问UI异常。

110,546

社区成员

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

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

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