WINFORM多线程优化问题,求指教

Zeus_zz 2018-04-16 07:30:18
我现在在做一个数据采集的软件,需要同时开100多个线程对100多台设备进行同时数据采集,全部通过COM口读取PLC。当只开20-30台左右设备的时候,软件正常,但超过一定数量或者时间之后,就会出现其中一些设备线程假死,线程不工作了,其它的还是正常,多线程接触的少,不知道该从哪里优化了。。。求指教。。。
代码部分:
设备是写的自定义控件,是动态创建的,一旦加载这个控件,相当于加载一台设备。
加载控件后,线程自动启动
isRun = true;
th_ex = new System.Threading.Thread(RunThread);
th_ex.IsBackground = true;
th_ex.Start();

线程内循环
private void RunThread()
{
while (isRun)
{
if (Operation.isRun)
{
run_time = DateTime.Now;
Exc_UpLoad();
}
System.Threading.Thread.Sleep(500);
}
}

部分 Exc_UpLoad代码
isEnd = false;
if (IsUse == "True")
{
int time_span = 500;
......................................................
.....................................................
....................................................
...........................
}
else
{
//
}
isEnd = true;
有用过Monitor来控制,但貌似没什么效果。

为了防止假死,我在另外在主窗体创建了一个线程进行监控这些线程
private void Auto_UploadData()
{
while (true)
{
if (Operation.isRun)
{
EX_Control();
}
//间隔
System.Threading.Thread.Sleep(2000);
}
}
而且加入了资源回收,控件清除再加载。。。
private void EX_Control()
{
foreach (Control us_c in panel1.Controls)
{
if (us_c is ucState)
{
ucState us = us_c as ucState;
if (us.IsUse == "True")
{
double ddtime = Operation.DateDiff(DateTime.Now, us.run_time);
if (ddtime > 60)//等待
{
//重置模块
try
{
ucState us_new = new ucState();
us_new = us;
us_new.Init_Data(us.dr_plc, dt_where, us.dt_plc_data);
us_new.run_time = DateTime.Now.AddSeconds(-120);
us.Dispose();
panel_Remove(us);
panel_add(us_new);
}
catch
{ }
}
}
}
System.Threading.Thread.Sleep(200);
}
GC.Collect();
}


最后还是没办法解决,刚开始还以为是交换机的问题,最后换了交换机也还是一样。。。。

请指教。。。。哪里需要改正。。。谢谢。
...全文
1001 17 打赏 收藏 转发到动态 举报
写回复
用AI写文章
17 条回复
切换为时间正序
请发表友善的回复…
发表回复
mk_lucifer 2018-04-18
  • 打赏
  • 举报
回复
引用 9 楼 Zeus_zz 的回复:
[quote=引用 7 楼 summergo123321 的回复:] 你这个模式是有问题的,其实数据采集只要一个线程就可以了。。设备数据采集依靠的是数据通讯,数据通讯可以通过异步模式进行。。。很多人总是认为多个任务就应该多线程,这总思维本身就是不对的,那我上万个用户岂不建立上万个线程???这都是不合理的。。。 最正常的操作逻辑是,一个线程轮询设备,主要是确定是否发应该送数据采集报文,如果要发送就启动一个针对该异步的通讯流程,这个很简单,在C++里你要依赖使用完成端口,在C#里你都不用知道完成端口是什么东西,直接调用异步通讯方法即可,通讯完成自动会回掉你的托管函数,然后你要做的就是打包接收的数据,扔给一个队列(不建议直接处理,因为此时占用了完成端口的线程,如果执行事件太长,只适合做效率高的简单处理,比如报文分析就可,保存数据库,更新UI就不可以,会阻碍通讯效率),有一个线程专门处理队列任务,将数据解析,然后进行处理,比如保存数据库,进行UI更新图纸等等,这才是正常的逻辑,如果嫌慢可以开4个线程处理队列消息,这是没问题的,但你的方法要100个线程,简直不能忍受的。。。
刚开始是单个线程轮循设备,但是设备太多的情况下,每个设备间隔的采集数据时间就越长,对方要求每秒都要对设备进行数据采集,而每台采集的数据并不是完全相同,动态配置的,按照单个线程的话根本没办法实现的,只能每台设备开一个线程,同类事务处理我都是放在同类线程里面的。 我在想,会不会是100个线程同时访问webserivce会引起的???[/quote] 既然你都说过你是访问PLC,PLC的思路逻辑本质上就是单线程,做N多事,他能这么做,主要得益于他的只有变量访问,没有事件等待,所有耗时操作,都封装成所谓的软件模块,PLC轮询一圈多少事件???好的都是微妙级吧,他要采集的数据并不比说的这100个少,但人家也是单线程,耗时(牵扯外设硬件)和不耗时(只牵扯逻辑)分开所有耗时无非就是等待硬件相应,等待本身CPU是什么都没做的,它一个线程,等一百个事也是等,等1个事也是等,为啥要安排100个人等呢??异步IO就是解决这个问题,所有IO在一个线程里等,哪个好了做哪个被。。。 首先要解决滥用多线程问题,首先要解决异步通讯,这个必须的,其次是安排一个合理的逻辑,让尽量少的线程解决,一个线程没问题,如果采集本身耗时很少,就像上上面说的,靠C#的异步机制,一个线程都不需要创建。。。
mk_lucifer 2018-04-18
  • 打赏
  • 举报
回复
引用 9 楼 Zeus_zz 的回复:
[quote=引用 7 楼 summergo123321 的回复:] 你这个模式是有问题的,其实数据采集只要一个线程就可以了。。设备数据采集依靠的是数据通讯,数据通讯可以通过异步模式进行。。。很多人总是认为多个任务就应该多线程,这总思维本身就是不对的,那我上万个用户岂不建立上万个线程???这都是不合理的。。。 最正常的操作逻辑是,一个线程轮询设备,主要是确定是否发应该送数据采集报文,如果要发送就启动一个针对该异步的通讯流程,这个很简单,在C++里你要依赖使用完成端口,在C#里你都不用知道完成端口是什么东西,直接调用异步通讯方法即可,通讯完成自动会回掉你的托管函数,然后你要做的就是打包接收的数据,扔给一个队列(不建议直接处理,因为此时占用了完成端口的线程,如果执行事件太长,只适合做效率高的简单处理,比如报文分析就可,保存数据库,更新UI就不可以,会阻碍通讯效率),有一个线程专门处理队列任务,将数据解析,然后进行处理,比如保存数据库,进行UI更新图纸等等,这才是正常的逻辑,如果嫌慢可以开4个线程处理队列消息,这是没问题的,但你的方法要100个线程,简直不能忍受的。。。
刚开始是单个线程轮循设备,但是设备太多的情况下,每个设备间隔的采集数据时间就越长,对方要求每秒都要对设备进行数据采集,而每台采集的数据并不是完全相同,动态配置的,按照单个线程的话根本没办法实现的,只能每台设备开一个线程,同类事务处理我都是放在同类线程里面的。 我在想,会不会是100个线程同时访问webserivce会引起的???[/quote] 我觉得你应该找一个多通讯任务并发的例程看一下,思路很重要,你并没有学会如何写一个通讯并发程序,也没有懂我的意思,你需要先学一下什么叫异步IO,学一下AsyncRead怎么用,而不是用Read,同步式虽然流程清晰,但每一步都在等待,是相当无效率得,不适合任务数量较多,效率要求较高得事情。。。华罗庚都教过统筹学,时间合理分配,最忌讳的是一个人照着流程说明书,按部就班得等每一个环节,这么做你只能加派人手,为什么就不能跳过做其他事,或者流水线式得呢。。。一个人可以同时干多件事情,而你的这种采集其实运算量是相当小,对于电脑来说再来1000个对单线程都不是问题,问题在于处理事务逻辑流程。。。
xuzuning 2018-04-18
  • 打赏
  • 举报
回复
使用线程池(ThreadPool)而不是 Thread 创建线程 ThreadPool 可使当前程序中的子线程均匀的获得运行机会,而 Thread 需要与操作系统中的所有线程抢夺时间片 不要在子线程中 Sleep,Sleep意味着放弃对 CPU 的控制权,从而失去好不容易等来的运行机会 尽可能的使用事件触发和异步处理,与外设交互的中断,永远是优先级最高的 锁(Lock)的代码越少越好,运行时间越短越好 如果 A 在锁定资源 R 后失去控制权,若接手的 B 恰恰也正需要资源 R,于是只能等待 A 去解锁,而此时 A 正在数千线程队列的尾部排队 长时间得不到运行机会或得到了又不得不放弃,这就是你说的 假死
  • 打赏
  • 举报
回复
引用 9 楼 Zeus_zz 的回复:
刚开始是单个线程轮循设备,但是设备太多的情况下,每个设备间隔的采集数据时间就越长,对方要求每秒都要对设备进行数据采集,而每台采集的数据并不是完全相同,动态配置的,按照单个线程的话根本没办法实现的,只能每台设备开一个线程,同类事务处理我都是放在同类线程里面的。
每个设备定时执行一个任务,(没到时间时)一个线程都不需要,定时到达时瞬间(从系统线程池)注册一个委托去发送一条采集指令(然后就结束了)。 要那么多线程干什么?一个线程都不需要!
Zeus_zz 2018-04-18
  • 打赏
  • 举报
回复
引用 11 楼 sp1234 的回复:
[quote=引用 9 楼 Zeus_zz 的回复:] 刚开始是单个线程轮循设备,但是设备太多的情况下,每个设备间隔的采集数据时间就越长,对方要求每秒都要对设备进行数据采集,而每台采集的数据并不是完全相同,动态配置的,按照单个线程的话根本没办法实现的,只能每台设备开一个线程,同类事务处理我都是放在同类线程里面的。
每个设备定时执行一个任务,(没到时间时)一个线程都不需要,定时到达时瞬间(从系统线程池)注册一个委托去发送一条采集指令(然后就结束了)。 要那么多线程干什么?一个线程都不需要![/quote] .NET3.0中怎么实现?由于对方兼容性问题,只能用3.0版本了,求指教
Zeus_zz 2018-04-18
  • 打赏
  • 举报
回复
引用 12 楼 xuzuning 的回复:
使用线程池(ThreadPool)而不是 Thread 创建线程 ThreadPool 可使当前程序中的子线程均匀的获得运行机会,而 Thread 需要与操作系统中的所有线程抢夺时间片 不要在子线程中 Sleep,Sleep意味着放弃对 CPU 的控制权,从而失去好不容易等来的运行机会 尽可能的使用事件触发和异步处理,与外设交互的中断,永远是优先级最高的 锁(Lock)的代码越少越好,运行时间越短越好 如果 A 在锁定资源 R 后失去控制权,若接手的 B 恰恰也正需要资源 R,于是只能等待 A 去解锁,而此时 A 正在数千线程队列的尾部排队 长时间得不到运行机会或得到了又不得不放弃,这就是你说的 假死
没有用LOCK。。。sleep是有
Zeus_zz 2018-04-18
  • 打赏
  • 举报
回复
引用 10 楼 zxh707wk 的回复:
电脑是连了一百多个串口还是一百多设备用一个串口?
100多个串口
707wk 2018-04-18
  • 打赏
  • 举报
回复
电脑是连了一百多个串口还是一百多设备用一个串口?
xian_wwq 2018-04-17
  • 打赏
  • 举报
回复
1.先关注下软件的资源占用情况 长时间运行监测内存占用,句柄数等, 如果有非托管资源,重点排查 2.try...catch中增加日志信息 否则异常时,看不到信息,也就不能及时定位bug 3.在线程中不能直接对UI进行操作,必须用委托; foreach中不能直接进行删除、添加操作,否则会报错。
exception92 2018-04-17
  • 打赏
  • 举报
回复
在主窗体创建了一个线程进行监控这些线程 -》画蛇添足?完全不需要,线程的使用是非常占用资源的,如果有上千的设备,岂不是要创建上千个线程??那这样电脑岂不是刚运行就卡死了。既然是通过COM口来采集数据,应该在数据接收事件里合理的处理缓冲区数据,之后擦除数据,保证数据的处理有一定规律,在接收事件中,异步处理回调函数可以保证数据的处理过程”不阻塞“。
Zeus_zz 2018-04-17
  • 打赏
  • 举报
回复
引用 7 楼 summergo123321 的回复:
你这个模式是有问题的,其实数据采集只要一个线程就可以了。。设备数据采集依靠的是数据通讯,数据通讯可以通过异步模式进行。。。很多人总是认为多个任务就应该多线程,这总思维本身就是不对的,那我上万个用户岂不建立上万个线程???这都是不合理的。。。 最正常的操作逻辑是,一个线程轮询设备,主要是确定是否发应该送数据采集报文,如果要发送就启动一个针对该异步的通讯流程,这个很简单,在C++里你要依赖使用完成端口,在C#里你都不用知道完成端口是什么东西,直接调用异步通讯方法即可,通讯完成自动会回掉你的托管函数,然后你要做的就是打包接收的数据,扔给一个队列(不建议直接处理,因为此时占用了完成端口的线程,如果执行事件太长,只适合做效率高的简单处理,比如报文分析就可,保存数据库,更新UI就不可以,会阻碍通讯效率),有一个线程专门处理队列任务,将数据解析,然后进行处理,比如保存数据库,进行UI更新图纸等等,这才是正常的逻辑,如果嫌慢可以开4个线程处理队列消息,这是没问题的,但你的方法要100个线程,简直不能忍受的。。。
刚开始是单个线程轮循设备,但是设备太多的情况下,每个设备间隔的采集数据时间就越长,对方要求每秒都要对设备进行数据采集,而每台采集的数据并不是完全相同,动态配置的,按照单个线程的话根本没办法实现的,只能每台设备开一个线程,同类事务处理我都是放在同类线程里面的。 我在想,会不会是100个线程同时访问webserivce会引起的???
mk_lucifer 2018-04-17
  • 打赏
  • 举报
回复
完整的通讯是, 通讯发起者+数据等待者+数据处理者,可以分别占一个线程,无论你多少设备这三个足以,甚至有时可以合并一个线程,毕竟事件等待你可以等0ms,如果你的事务处理又是耗时超短的话,三个角色可以合并一个线程,但逻辑上这三个职责的程序必须都存在,多少设备都没问题。。 1.通讯发起者,主要用来发起异步通讯任务。 2. 数据等待者,即等待完成端口的线程,没别的事,就是等待一项通讯任务结束,然后打包扔给处理者,一个完成端口可以用来绑定很多IO操作,包括文件读写,网口,串口等等所有IO操纵,在C#里这个东西你可以不用关心,他是隐藏在后台的,你只要对IO对象进行异步读写,就自动在使用它。。 3. 数据处理者,这个线程可以多个,主要是从队列里取一个数据包,然后分析数据是什么,然后开始你的业务逻辑。。。
mk_lucifer 2018-04-17
  • 打赏
  • 举报
回复
你这个模式是有问题的,其实数据采集只要一个线程就可以了。。设备数据采集依靠的是数据通讯,数据通讯可以通过异步模式进行。。。很多人总是认为多个任务就应该多线程,这总思维本身就是不对的,那我上万个用户岂不建立上万个线程???这都是不合理的。。。 最正常的操作逻辑是,一个线程轮询设备,主要是确定是否发应该送数据采集报文,如果要发送就启动一个针对该异步的通讯流程,这个很简单,在C++里你要依赖使用完成端口,在C#里你都不用知道完成端口是什么东西,直接调用异步通讯方法即可,通讯完成自动会回掉你的托管函数,然后你要做的就是打包接收的数据,扔给一个队列(不建议直接处理,因为此时占用了完成端口的线程,如果执行事件太长,只适合做效率高的简单处理,比如报文分析就可,保存数据库,更新UI就不可以,会阻碍通讯效率),有一个线程专门处理队列任务,将数据解析,然后进行处理,比如保存数据库,进行UI更新图纸等等,这才是正常的逻辑,如果嫌慢可以开4个线程处理队列消息,这是没问题的,但你的方法要100个线程,简直不能忍受的。。。
xian_wwq 2018-04-17
  • 打赏
  • 举报
回复
引用 3 楼 Zeus_zz 的回复:
2.基本每部都有日志,没有抛出异常,就直接不执行了。
有日志就好办,可以大致定位线程运行到哪个部分异常停止的 可以缩小排查问题的范围
Zeus_zz 2018-04-17
  • 打赏
  • 举报
回复
有没有人知道啊
Zeus_zz 2018-04-17
  • 打赏
  • 举报
回复
引用 2 楼 duanzi_peng 的回复:
在主窗体创建了一个线程进行监控这些线程 -》画蛇添足?完全不需要,线程的使用是非常占用资源的,如果有上千的设备,岂不是要创建上千个线程??那这样电脑岂不是刚运行就卡死了。既然是通过COM口来采集数据,应该在数据接收事件里合理的处理缓冲区数据,之后擦除数据,保证数据的处理有一定规律,在接收事件中,异步处理回调函数可以保证数据的处理过程”不阻塞“。
并不是每一个都创建了监控线程,只创建了一个监控线程,查看设备操作是否超时,COM采集之前都会清空之前的缓存,现在问题不是出现在数据采集这里,而是部分线程无缘无故就不执行了,没有错误抛出。
Zeus_zz 2018-04-17
  • 打赏
  • 举报
回复
引用 1 楼 xian_wwq 的回复:
1.先关注下软件的资源占用情况 长时间运行监测内存占用,句柄数等, 如果有非托管资源,重点排查 2.try...catch中增加日志信息 否则异常时,看不到信息,也就不能及时定位bug 3.在线程中不能直接对UI进行操作,必须用委托; foreach中不能直接进行删除、添加操作,否则会报错。
1.资源占用情况还算稳定,资源都及时回收了 2.基本每部都有日志,没有抛出异常,就直接不执行了。 3.对UI操作都是用的委托,也只有一个地方对UI进行了操作,过程中也没有数据库操作。只有对webservice的访问。

110,538

社区成员

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

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

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