多线程改变Ui界面

6lilu9 2021-04-05 04:57:39
以下过程是我整个程序的一段缩码,
大体意思就是一个文本框,想多线程开几个任务,同时每个任务完成后显示在文本框中,
但现在的结果是文本框根本不显示,只显示最后一个结果49,而我要求从1到49滚动(当然我也知道不一定是按顺序来),


private async void button1_Click(object sender, EventArgs e)
{
await Do(); //
Console.WriteLine("全部完成了");
}

public async Task Do()
{
Console.WriteLine($"当前主线程的ID是{Thread.CurrentThread.ManagedThreadId}");

List<Task> TaskList = new List<Task>();
var scheduler = new LimitedConcurrencyLevelTaskScheduler(70);
for (int i = 0; i < 50; i++)
{
Console.WriteLine($"当前{i}的ID是{Thread.CurrentThread.ManagedThreadId}");
Thread.Sleep(100);
TaskList.Add(Task.Factory.StartNew(t =>
{
//这里插一个大量耗时过程
textBox1Set(Convert.ToInt32(t));
}, i, CancellationToken.None, TaskCreationOptions.None, scheduler));
}
await Task.WhenAll(TaskList.ToArray());
Console.WriteLine($"结束了");
}

public void textBox1Set(int I)//供当前及后续类写日志用
{
Console.WriteLine($"当前{I}的textBox1SetID是{Thread.CurrentThread.ManagedThreadId}");
textBox1.Invoke((EventHandler)delegate
{
textBox1.Text = I.ToString();
Console.WriteLine($"当前{I}的textBox1SetID的BeginInvoke是{Thread.CurrentThread.ManagedThreadId}");
});
}




网上搜了半天,大体搞明白了原因是多线程把UI阻塞了(具体什么原因也不懂)

我想把“ await Do(); // ”改为 await Task.Run(() => { Do(); });
却有提示“此调用不会等待”; 也不敢用,请高手指点。



...全文
258 4 打赏 收藏 转发到动态 举报
AI 作业
写回复
用AI写文章
4 条回复
切换为时间正序
请发表友善的回复…
发表回复
wanghui0380 2021-04-05
  • 打赏
  • 举报
回复
最后的问题是这样。 前面说了,异步的实质是IO等待。IO等待属于IO操作理论上不消耗cpu,而线程的本质是“cpu并发”属于需要耗CPU,所以我们只是选择不耗CPU的就不耗cpu。 同时因为await操作,所以使用run,因为run是对startnew的简化封装,他内部其实改写成了 await task<task>这种方式,同时还改写了启动参数,所以不会对你后面的waitall产生影响
6lilu9 2021-04-05
  • 打赏
  • 举报
回复
引用 2 楼 wanghui0380 的回复:
Thread.Sleep(100); 请先把这个去掉 ,因为你说的是“模拟一个大量耗时的动作”,但其实你后面的代码根本就不会耗时 。。。。。。。i, Cancel
非常感谢,我琢磨透了,作为这个问题本身,你给我指出了根本的原因,那就是我本身的代码有问题,那个耗时过程(Thread.Sleep(100);)我放错位置了,导致多线程已经运行完了,Ui才结束,所以肯定显示最终结果49,作为解决方案,我其实只要把这个过程换个位置不就行了,如下,已测试通过,满足我要求

        public async Task Do()
        {
            Console.WriteLine($"当前主线程的ID是{Thread.CurrentThread.ManagedThreadId}");

            List<Task> TaskList = new List<Task>();
            var scheduler = new LimitedConcurrencyLevelTaskScheduler(70);
            for (int i = 0; i < 50; i++)
            {
                Console.WriteLine($"当前{i}的ID是{Thread.CurrentThread.ManagedThreadId}");              

                TaskList.Add(Task.Factory.StartNew(t =>
                {
                    Thread.Sleep(100);
                    textBox1Set(Convert.ToInt32(t));
                }, i, CancellationToken.None, TaskCreationOptions.None, scheduler));
            }
            await Task.WhenAll(TaskList.ToArray());
            Console.WriteLine($"结束了");
        }
作为延申,我实在没明白,为什么要把“task.factory.startnew ”改为“task.run”,虽然你解释了(防止你那个waitall不带入问题),但还是没懂。 还有,为什么把“ Thread.Sleep(1000);”改为“asycn await Task.Delay(2000) ” 我上面用法不是也挺好吗? 再次感谢!
wanghui0380 2021-04-05
  • 打赏
  • 举报
回复
Thread.Sleep(100); 请先把这个去掉 ,因为你说的是“模拟一个大量耗时的动作”,但其实你后面的代码根本就不会耗时 而你每次循环来个sleep其实是让主线程睡觉,但是后面子task早就完成了,那么最后的结果其实就是子task早就是完成到49了,而你的主线程才刷新。 你前一个帖子我们已经说过了,如果不到另外启动一个task的情况,他其实只是一个io等待,他不会搞啥启动新线程,所以这个sleep其实是让主线程休眠 你想达到你的目的,请这样写 TaskList.Add(Task.run(asycn t => { //这里插一个大量耗时过程 await Task.Delay(2000);//到了这个Task,他才会进入线程池,同时这个耗时动作,当然在这个task里模拟,这里用task.run是防止你那个waitall不带入问题,我们已知task.factory.startnew 默认不带上下文,尤其是里面还有await的异步情况 textBox1Set(Convert.ToInt32(t)); }, i, Cancel
desperaso 2021-04-05
  • 打赏
  • 举报
回复
private class UISync { private static ISynchronizeInvoke Sync; public static void Init(ISynchronizeInvoke sync) { Sync = sync; } public static void Execute(Action action) { Sync.BeginInvoke(action, null); } } void XXXXX(object sender, EventArgs e) { UISync.Execute(() => XXXXXXXXXXXXXXXXXXX); }

111,097

社区成员

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

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

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