勇哥注:
我们继续上一篇《C# 勇哥关于多线程读写plc内存的研究续,解决lock锁的效率问题》
上一篇我们解决了lock锁的效率问题。
本节我们继续讨论一下控件读写的效率问题。
由下图中,我们看到的RichTextBox.SetText,它占6.44%的效率。
本着程序员的工匠精神,我们也要把这部分耗能解决掉。
直接说答案:
我们在上节代码中,使用了Invoke输出RichTextBox的文本。
也就是使用委托来输出控件信息。
RtbMsg.Invoke(new Action(() =>
{
RtbMsg.Text = ($"{plc.Read(n, Guid.NewGuid()).ToString()}\n");
}));
此方式,其实也是一种阻塞方式,此方式工作的时候,你可以尝试拖动winform窗口,会明显感觉到拖动不流畅,有一顿一顿的感觉。
我们为了追求极致的ui流畅性,建议使用BeginInvoke。
两的区别总结如下:
1、Invoke() 调用时,Invoke会阻止当前主线程的运行,等到 Invoke() 方法返回才继续执行后面的代码,
表现出“同步”的概念。
2、BeginInvoke() 调用时,当前线程会启用线程池中的某个线程来执行此方法,
BeginInvoke不会阻止当前主线程的运行,而是等当前主线程做完事情之后再执行BeginInvoke中的代码内容,
表现出“异步”的概念。在想获取 BeginInvoke() 执行完毕后的结果时,
调用EndInvoke() 方法来获取。而这两个方法中执行的是一个委托。
勇哥把原来的invoke改成下面一个函数:
private void outMsg(string msg)
{
if (RtbMsg.InvokeRequired)
{
//第一种写法
Action<string> actionDelegate = (x) => { RtbMsg.Text = msg; };
//第二种写法
//Action<string> actionDelegate = delegate (string txt) { textBox2.AppendText("1"); };
// 1、Invoke方法是同步的, 它会等待工作线程完成,会阻塞其他子线程
// this.textBox2.Invoke(actionDelegate, str);
//2、BeginInvoke方法是异步的, 它会另起一个子线程去完成工作线程
this.RtbMsg.BeginInvoke(actionDelegate, str);
}
else
{
RtbMsg.Text = msg;
}
}
再次执行代码:
这次的cpu资源耗能大户已经没有了RichTextBox.SetText
除了Application.Run外,其它的都是系统对象了。
经过三轮的反复优化,这个PlcReadWrite类已经完成。
它经过修改,也可以用来实现多线程 操作串口。
即我们的程序使用多线程也可能读写同一个硬件串口资源。
但是串口读写是无协议的,因此会产生读写缓存的结果和读写指令匹配的问题。
因此其实也是殊为不易的。
有空,勇哥继续讨论这个话题。
---------------------
作者:hackpig
来源:www.skcircle.com
版权声明:本文为博主原创文章,转载请附上博文链接!

