首页 > 其他分享 >WinForm(七)在新线程中更新UI

WinForm(七)在新线程中更新UI

时间:2022-12-13 21:02:27浏览次数:50  
标签:list mark item 线程 var UI WinForm dotString

  在WinForm项目中,很多时候会映遇上多线程一起工作的情况,因为当前UI的更新显示,是在主线程中,一但主线程被长时运算占据后,UI就会被卡信,出现假死现像。那么就需要起一个新线程做长时运算工作,把进度或数据同步回UI线程。

  以一个医保上传数据为例,功能是同步药品,器械,诊疗项目,同步完后进行验证核对。

  注:为了看得清晰,各个关键控件我没有重命名

  定义一个list来充当步骤和需要时间。

static List<Item> list;
private void Form1_Load(object sender, EventArgs e)
{
list = new List<Item>
{
new Item{ Name="正在上传诊疗项目",Time=8 },
new Item{ Name="正在上传器材",Time=12 },
new Item{ Name="正在上传药品",Time=20 },
new Item{ Name="正在核对",Time=24 },
};
}

  第一版:用Task.Run来启动一个新的线程,中的for循环是为了表示一个进度的...的切换,如果换成一个gif图标更佳,foreach循环是完成list中各项任务的。如果运行,你会发现,窗体上是空白的,18行的给messageLabel.Text不起作用。

private void button1_Click(object sender, EventArgs e)
{
Task.Run(() =>
{
foreach (var item in list)
{
var dotString = "";
for (var i = 0; i < item.Time; i++)
{
if (i % 6 == 0)
{
dotString = ".";
}
else
{
dotString += ".";
}
messageLabel.Text = $"{item.Name}{dotString}";
SpinWait.SpinUntil(() => false, 300);
}
}
MessageBox.Show("完成医保所有数据同步");
});
}

  第二版:为了更新UI线程,在新线程中用this.Invoke来更新UI上控件的值,如18行。这回没有什么问题了,UI也能即时更新,看上去很完美。

private void button2_Click(object sender, EventArgs e)
{
Task.Run(() =>
{
foreach (var item in list)
{
var dotString = "";
for (var i = 0; i < item.Time; i++)
{
if (i % 6 == 0)
{
dotString = ".";
}
else
{
dotString += ".";
}
this.Invoke(() =>
{
messageLabel.Text = $"{item.Name}{dotString}";
});
SpinWait.SpinUntil(() => false, 300);
}
}
MessageBox.Show("完成医保所有数据同步");
});
}

  其实这背后是有异常的(有可能会在vs中报出来),因为当你关闭窗体时,18行的this已经不存在了,但访问this.Invoke在新的线程中,新线程本身并没有关掉,这时就会报找不到实例,有可能你运行起来并不会发现异常,这是因为主线程关闭后,所有创建的子线程都会关闭的,为了验证,你可以5行前面加一个try,在catch中把异常内容写在一个日志文件中。

  我的结果是:

Cannot access a disposed object.
Object name: 'Form1'.

  第三版:发现问题,那就解决吧(有可能你觉得无所谓,窗体都关闭了,没报异常,看不出来。确实,如果你觉得无所谓,那就无所谓,这里只是来说明技术点的,不是来说服想法的),通过一个mark标志,在关闭窗体时,拦截一下,把子线程关闭,然后再把主窗体关闭,这样就没有问题了,自己起的线程,自己要关掉。

static bool mark = true;
private void startButton_Click(object sender, EventArgs e)
{
Task.Run(() =>
{
foreach (var item in list)
{
if (mark == false)
{
break;
}
var dotString = "";
for (var i = 0; i < item.Time; i++)
{
if (mark == false)
{
break;
}
if (i % 6 == 0)
{
dotString = ".";
}
else
{
dotString += ".";
}
this.Invoke(() =>
{
messageLabel.Text = $"{item.Name}{dotString}";
});
SpinWait.SpinUntil(() => false, 300);
}
}
if (mark)
{
mark = false;
MessageBox.Show("完成医保所有数据同步");
}
else
{
this.Invoke(() =>
{
this.Close();
});
}
});
}
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
if (mark)
{
e.Cancel = true;
mark = false;
}
}

  线程是个大话题,这只是简单的线程协同,两个线程没有深度协同。通过这个例子,三个版本,一步一步来满足功能,提升健壮性和安全性,我们们可以得出,做了一个功能,是需要下功夫的,不但要知其然,还要知其所以然。

  想要更快更方便的了解相关知识,可以关注微信公众号

WinForm(七)在新线程中更新UI_新线程中更新UI

 

标签:list,mark,item,线程,var,UI,WinForm,dotString
From: https://blog.51cto.com/axzxs/5935092

相关文章

  • WinForm(八)窗体,窗体
    我们在控件那篇文章里说过,窗体和控件都是一个类,项目中一个个窗体,都是Form类的子类。关于这个类有几个重要的成员,也是最常用成员,以供初学者了解:Load事件:发生在构造函......
  • WinForm(七)在新线程中更新UI
    在WinForm项目中,很多时候会映遇上多线程一起工作的情况,因为当前UI的更新显示,是在主线程中,一但主线程被长时运算占据后,UI就会被卡信,出现假死现像。那么就需要起一个新线......
  • WinForm(八)窗体,窗体
    我们在控件那篇文章里说过,窗体和控件都是一个类,项目中一个个窗体,都是Form类的子类。关于这个类有几个重要的成员,也是最常用成员,以供初学者了解:Load事件:发生在构造......
  • WinForm(八)窗体,窗体
    我们在控件那篇文章里说过,窗体和控件都是一个类,项目中一个个窗体,都是Form类的子类。关于这个类有几个重要的成员,也是最常用成员,以供初学者了解:Load事件:发生在构造......
  • WinForm(七)在新线程中更新UI
    在WinForm项目中,很多时候会映遇上多线程一起工作的情况,因为当前UI的更新显示,是在主线程中,一但主线程被长时运算占据后,UI就会被卡信,出现假死现像。那么就需要起一个新线......
  • vue的elementui的时间控件 如何设置输出时间格式(el-date-picker:日期选择器、日期时间
    注:使用value-format<el-date-pickerclass="topfontstyle"v-model="listMain.time"type="daterange"......
  • Java中HashMap有哪些方式可以保证线程安全【杭州多测师】【杭州多测师_王sir】
    hashmap不是线程安全的,有提供两种方法让hashmap支持线程安全方法一:通过Collections.synchronizedMap()返回一个新的map,这个新的map是线程安全的,要求大家习惯基于接口编程,......
  • Java中ConcurrentHashMap,HashMap和HashTable区别,通过ConcurrentHashMap对key进行加锁
    一、什么是ConcurrentHashMapConcurrentHashMap和HashMap一样,是一个存放键值对的容器。使用hash算法来获取值的地址,因此时间复杂度是O(1)。查询非常快。同时,ConcurrentHash......
  • 网页唤起Winform窗体通过非IE浏览器
    1、简介本文主要介绍非IE浏览器的ActiveX控件替换方案.常用的做法是通过注册表来注册URL协议来完成这个功能,像腾讯的Tim等软件就是如此,如下图  所以,第一步就是通......
  • Qt子线程中使用UI线程
    Qt子线程中使用UI线程方案起源最近做了一个Excel保存图表的项目,因为不能直接用Excel的图表(会直接暴露计算数据),所以采用的是QCharts生成的表格,但是QCharts的问题是调用Q......