关于C# WPF中的DoEvents,要稳定且快速的,哪位有比较完美的解法?

wShineboy 2018-06-07 04:31:21
RT:使用C#时,相信各位童鞋在WinFrom中没少用Application.DoEvents(如同VB6的DoEvents)。但到了WPF中,这个东西就莫名其妙的被MS忽略了。

在长时间循环处理中,进程阻塞实在难免,网上也有不少DoEvents的解法,效果可以达到,但不是不稳定就是影响速度。下面这一串程序可能比较普及,可惜它不是很稳定,有时会导致窗体停止响应。
        private static DispatcherOperationCallback exitFrameCallback = new DispatcherOperationCallback(ExitFrame);

/// <summary>
/// Processes all UI messages currently in the message queue.
/// </summary>
public static void DoEvents()
{
DispatcherFrame nestedFrame = new DispatcherFrame();
DispatcherOperation exitOperation = Dispatcher.CurrentDispatcher.BeginInvoke(DispatcherPriority.Background, exitFrameCallback, nestedFrame);
Dispatcher.PushFrame(nestedFrame);
if (exitOperation.Status != DispatcherOperationStatus.Completed)
{
exitOperation.Abort();
}
}

private static Object ExitFrame(Object state)
{
DispatcherFrame frame = state as DispatcherFrame;
// Exit the nested message loop.
frame.Continue = false;
return null;
}


后来俺找了个老外写的东西,也能达到类似效果,不过好像有点拖速度——俺的程序需要实时同步单片机的数据,时间很宝贵。
就是这个——
Application.Current.Dispatcher.Invoke(DispatcherPriority.Background, new System.Threading.ThreadStart(delegate { }));
...全文
2007 17 打赏 收藏 转发到动态 举报
写回复
用AI写文章
17 条回复
切换为时间正序
请发表友善的回复…
发表回复
guojun0718 2020-07-04
  • 打赏
  • 举报
回复
想想一下,示波器怎么实时显示曲线?1K算个屁。示波器都上G了。
zslefour 2019-09-11
  • 打赏
  • 举报
回复
对于一个业余的编程爱好者来说,还未接触过异步,年过半百的我,现在开始去了解下异步看看。
编程有钱人了 2018-06-13
  • 打赏
  • 举报
回复
异步,异步,异步重要的事情说三遍,等你用熟练了,你会发现这个东西真好
  • 打赏
  • 举报
回复
纠结”1KHz“的前提是整个系统是同步、卡死主线程式的编程。比如说我的机器当前 windows 下有2000个线程,假设你应要只有20个线程做所有的事情,你认为这样就能保证每一个线程的”严格的时间顺序“,那么其实你就不适合使用说面 windows 这类操作系统,你应该使用比较原始的、专门为工控机开发的操作系统啊!!!!
  • 打赏
  • 举报
回复
比如说你在一个 Mouse_Move 事件处理过程中调试,刚处理2行代码,突然掉入了 Key_Down 事件处理过程中,然后突然又调入了定时器事件处理过程中,然后又跳回 Mouse_Move 事件过程,然后发现又处理了下一个 Mouse_Move 事件,然后又发现又跳回了第一个 Mouse_Move 事件处理阶段(因为 DoEvents 的原因,第一个事件处理还没有完毕,就混乱地迭代无数其它的事件过程中,然后有跳回)。 ”稳定且快速“这可能是指单线程编程习惯下的编程。但是单线程下,你就更加不应该考虑什么 DoEvents。单线程下要做到”稳定“你就更得保证事件处理过程是按照严格的顺序执行的,千万不能递归迭代胡乱——随机——从中间事件处理过程中去陷入其它事件处理过程,否则程序就根本不是预期的可调式的结果。除非你的程序非常小非常简单才用DoEvents。
  • 打赏
  • 举报
回复
以消息的调用轮询方式为代价的 DoEvents——不支持异步处理时——它不仅仅是一般地处理了消息,而是把消息的正常地顺序控制逻辑给混乱成为了高度嵌套递归的逻辑。比如说事件A、B、C、D.......原本是顺序触发的,当你在 A 中写了一个 DoEvents,A事件响应过程中递归嵌入了B、C、D.....的处理逻辑,特别是有的人甚至还喜欢在一个 while 循环中去使用 DoEvents 那么就更惨了,事件完全可能递归爆发(当你的代码会触发事件发生时)、并且次序混乱(在主线程完全诡异地前后颠倒几十个事件次序)、内存使用暴增、主线程卡死。
蒋晟 2018-06-12
  • 打赏
  • 举报
回复
DoEvents本身是启动另一个消息循环,不做异步的话,只能改善界面的更新,但是是以拖慢程序为代价的,因为插入了消息循环的代码。 多核计算机上,每个线程顶多能占一个核,要充分发挥机器性能,必须做异步。
xuzuning 2018-06-12
  • 打赏
  • 举报
回复
相信你的机器的上网速度都是每秒数兆了,(再差的套餐也是几百K)。为何要纠结 1KHz 能否保证速度呢? 不行的是你,而不是机器
wShineboy 2018-06-12
  • 打赏
  • 举报
回复
MCU启动后,它采AD的速度可以达到1KHz以上,发到上位机的数据美妙只有一两百帧,即使实时循环也可能来不及接收。异步能保证速度吗?
  • 打赏
  • 举报
回复
引用 楼主 wShineboy 的回复:
在长时间循环处理中,进程阻塞实在难免,
有事件就处理事件,没事件还循环什么?这完全说明了你不是事件驱动的设计,而是死循环去轮询的习惯。 事件处理过程中,为了不阻塞 UI 线程,那么就应该采用异步方式来处理。假设事件本来就发生在 I/O 或者子 Work 线程,而不是 UI 线程,那么它本身也就不可能卡死 UI 刷新。在异步子线程中,要访问 UI 控件就得使用 control.BeginInvoke 方式,从这类代码“有没有”就能看到你设计的出发点是什么样的。
  • 打赏
  • 举报
回复
现在的 .net 编程知识,可能把 Task 作为一个核心能力来看待,例如
var data = await ReceiveDatas(.....);
去UI显示(data);
这类省得写 BeginInvoke 的代码也很常见了。这里的关键就在于 await 机制,根本不是阻塞的,而是异步的。这样当异步回调而获得 data 之后,又去主线程进行显示 data 操作了。 关键就在于异步知识。不是死守着同步、阻塞思路去“设计解法”。
  • 打赏
  • 举报
回复
引用 楼主 wShineboy 的回复:
RT:使用C#时,相信各位童鞋在WinFrom中没少用Application.DoEvents(如同VB6的DoEvents)。但到了WPF中,这个东西就莫名其妙的被MS忽略了。
VB(不是 vb.net)根本不支持异步多线程,所以它使用 Application.DoEvents 方法来打乱 windows 消息泵的响应次序(使得一个 UI 消息处理过程中强行执行后边的消息)。这本来是 VB 的糟粕,是不得已才这样写程序的。 到了 .net 时代,程序员要学会异步、多线程去编程,并且在子线程需要访问 UI 控件时使用 control.BeginInvoke/Invoke 委托方式。这才是合适的并且不打乱 windows 消息次序的做法。
cheng2005 2018-06-08
  • 打赏
  • 举报
回复
除了把数据显示到界面上这一步,你的所有逻辑都和UI无关,为什么要放到主线程里?还要用DoEvents()... 另外,“每秒200帧数据”不是说你每秒要刷新200次UI,你又不是超人眼,一点意义都没有。24帧的电影你看的还不够流畅? 好好规划你的逻辑,而不是用什么奇技淫巧来做一个大玩具。
wShineboy 2018-06-08
  • 打赏
  • 举报
回复
哈,打字太快了——应该是循环处理阻塞了UI,导致界面假死。 大家看看这样的流程,能不能启一个辅助线程来跑。 给下位机发命令让它开始工作,其工作过程中以1KHz的速度采样,每秒发200帧数据到PC。上位机软件须即时显示其采样数据并绘制曲线,直到单片机完成工作流程。 即:下位机成功收到命令后开始工作,以“停止发送AD”这样的方式告诉上位机工作完成。
while (true)
            {
                while(SerialPort.BytesToRead <XXX)
                {
                    //串口可读取数据不足则等待,等待超时则认为工作流程结束,结束时return
                }
                DoEvents();

                //接收AD,经转换后显示数值并绘制实时曲线
                //刷新曲线和数据,每秒三次即可保证连贯性,刷新后须 DoEvents
                //……………其它处理…………………………………
            }
圣殿骑士18 2018-06-07
  • 打赏
  • 举报
回复
偷懒才用Application.DoEvents
exception92 2018-06-07
  • 打赏
  • 举报
回复
进程怎么能阻塞,进程表示操作系统为运行PE32文件(PE32+)而分配一个虚拟地址空间,用它来确保程序之间的运行互不影响,提供一个界定的区域。
Invoke 使用线程同步执行第二个参数中的逻辑,导致UI显示更新不及时。 同步单片机数据 并不一定非得要接受一次就必须处理一次,可以加入到队列集合中,然后去依次处理队列集合中的数据。
cheng2005 2018-06-07
  • 打赏
  • 举报
回复
除非做一些和UI相关的特殊功能(非常个性化的需求),不然应该用不到Application.DoEvents。

110,536

社区成员

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

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

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