引言
在笔者参与的四川省重点污染源企业环境远程监控系统中,有一项非常重要的工作:将多达80台的远程DVS(视频服务器)的监测数据通过因特网传输,由上位机收集上来,写入SQL Server 2005数据库中。远程数据每隔一分钟发送一次实时数据。如果数据在一分钟内传送不成功,那么DVS将认为网络已经断开,又要不断的发起新的连接。因此,上位机能不能及时的准确的收集、写入,是系统成败的关键。
项目分析
80多台远程DVS正在不间断的采集数据,在网络正常的情况下,会不间断的向上位机发送数据。如果采用传统的单线程结构,上位机接受连接请求,接收处理数据,将数据写入数据库,然后再接受新的连接请求,接收处理数据,……,这样,上位机程序异常繁忙,CPU利用率几乎将达100%。由于服务器不能迅速处理请求,DVS只好等待。
更为重要的是,为了减少上位机发送响应连接的次数,设备采用的是长连接,即发送一次连接请求并得到响应后,发送数据时不再发送连接请求。因此,要求上位机能够保存客户端的Socket。
为了避免这种情形发生。笔者采用了异步、多线程来处理。所谓异步,是程序调用一个方法后立即返回,总体而言,主线程与方法线程并行执行。而同步即程序执行一个方法,等该方法返回之后,继续往下走,本系统从功能上分成3个模块,即3个前后关联的线程:主线程、数据接收线程、存入数据库线程,它们异步执行。
主线程
主线程工作流程如图一所示。其主要功能是:初始化参数,如连接端口号、IP地址等,侦听连接请求,将传入的连接保留到TcpClient对象数组sockets,而这个数组sockets恰恰是我们后面线程中要用到的全局变量。 为了不使线程间争用这个数组变量,这里用到了VB.net提供的Monitor类,它提供同步对象的访问的机制。

当主线程侦听到远程DVS有连接请求时,立即执行AcceptTcpClient方法,创建一个TcpClient实例,并将它放入sockets数组。同时创建线程对象serverthread。

声明创建线程时,使用 ThreadStart 委托作为其唯一参数的构造函数创建 Thread 类的新实例,创建线程时需要传递处理连接的过程或函数的地址以被线程调用。创建线程委托,传递需要操作的过程的地址,这部分的代码如下所示:
Public Sub WaitData()
Try
Dim ipHostInfo As IPHostEntry = Dns.Resolve(Dns.GetHostName())
Dim localAddr As IPAddress = ipHostInfo.AddressList(0)
s = New TcpListener(localAddr, ListenPort)
s.Start()’开始侦听连接请求
Dim Recdatathread As New Thread(New ThreadStart(AddressOf RecDataProc)) ’创建数据接收线程
Recdatathread.IsBackground = True
Recdatathread.Start()’启动线程
While True
Dim client As TcpClient = s.AcceptTcpClient()
Monitor.Enter(sockets) '在指定对象上获取排他锁
sockets(socketcount) = client
socketcount = socketcount + 1
Monitor.Exit(sockets) '释放指定对象上的排他锁
End While
Catch e As SocketException
s.Stop()
saveErrLog(Date.Now, CType(s.AcceptTcpClient.Client.RemoteEndPoint, IPEndPoint).Address.ToString(), e.Message)’写入错误日志
Catch e As ThreadAbortException
t.Abort()
saveErrLog(Date.Now, CType(s.AcceptTcpClient.Client.RemoteEndPoint, IPEndPoint).Address.ToString(), e.Message) ’写入错误日志
Finally
t.Abort()
End
End Try
End Sub
数据接收线程
数据接收线程的工作流程如图二所示。主要功能是:将挂起连接的DVS上传数据从流中读取出来,创建数据写入线程,并在listbox中显示。
从保存的socket数组中读取字节流时,必须考虑以下问题:
一、有些DVS可能会在工作一段时间后发生设备故障或者网络中断,但服务器保存的是其历史socket,因此,必须判断其connect属性,即设备是否在线。
二、为了减少服务器的空等时间,必须判断流对象(stream)的DataAvailable属性。
三、创建线程saveToDb时,必须考虑传入参数的问题。通常的线程创建是不可提供参数的。我们将线程saveToDb的执行体封装到一个类中,通过初始化类的成员变量的方法,来达到传送参数的目的。
四、由于本线程是长驻内存并循环执行的。因此,应当在适当的地方阻止,否则,CPU的利用率将达几乎100%。
这部分的代码如下:
Public Sub RecDataProc()
Dim i As Integer
Dim c As TcpClient
While (True)
Try
For i = 0 To socketList.Count - 1
If socketList.Item(i).client.connected Then '如果该连接在线
Dim dh1 As DelegateHandler = New DelegateHandler(AddressOf displayStatusBarPanel2)
'New 出一个委托并指定委托方法
Me.Invoke(dh1, New Object() {CStr(i)}) '调用invoke方法
c = socketList.Item(i)
Dim stream As NetworkStream = c.GetStream()
If stream.DataAvailable Then
Dim dh As DelegateHandler1 = New DelegateHandler1(AddressOf ShowInBox)
Dim readbuff As New ReadBuffClass(c, stream, Connection, dh) '由构造函数来初始化成员变量
ThreadPool.QueueUserWorkItem(New WaitCallback(AddressOf readbuff.ReadBuff), readbuff)’把具体从流中读取数据的工作交给线程池的线程来进行
Dim workerThreads, portThreads As Integer
ThreadPool.GetAvailableThreads(workerThreads, portThreads)
Dim dh2 As DelegateHandler = New DelegateHandler(AddressOf displayStatusBarPanel4)
'New 出一个委托并指定委托方法
Me.Invoke(dh2, New Object() {workerThreads.ToString}) '调用invoke方法
End If
Thread.Sleep(20) '如果不阻止,则CPU利用率将为100%
End If
Next
Catch ex As System.ArgumentOutOfRangeException
Catch ex As System.InvalidOperationException
Catch ex As ObjectDisposedException 'TcpClient 已关闭
Catch ex As SocketException
Catch ex As ThreadAbortException
Catch ex As System.IO.IOException
Catch ex As System.AccessViolationException
Finally
End Try
End While
End Sub
数据处理线程
这部份线程每个都由线程池来调度运行。由于要接收线程参数,因此,线程本身被封装到一个类中,限于篇幅的原因,只描述类的结构。
Public Class ReadBuffClass
Private sck As TcpClient
Private ns As NetworkStream
Private sqlcnn As SqlConnection
Private delg As frmServerMain.DelegateHandler1

Dim sqlcmd As SqlCommand

Dim sqlda As SqlDataAdapter

Public Sub New(ByVal sc As TcpClient, ByVal n As NetworkStream, ByVal cn As SqlConnection, ByVal dh As frmServerMain.DelegateHandler1) '由构造函数来初始化成员变量
Me.sck = sc
Me.ns = n
Me.sqlcnn = cn
Me.delg = dh
End Sub

Public Sub ReadBuff(ByVal state As Object) ’ 线程的入口函数
Dim datastring As String = “”
ns.ReadTimeout = 100 '读取失败前经历的毫秒数
Try
While (True)
Dim bytes(2048) As Byte
ns.Read(bytes, 0, 2048)
datastring = datastring + Encoding.ASCII.GetString(bytes)
If datastring.IndexOf(vbCrLf) > 0 Then
Exit While
End If
End While
delg.Invoke(datastring, sck) '通过委托的方式,将参数传给UI
Dim tmparr() As String = datastring.Split("##")
Dim i As Integer
For i = 0 To tmparr.Length - 1
If tmparr(i) <> “” Then
ProcessInfo(tmparr(i))
End If
Next
Catch ex As System.AccessViolationException
Catch ex As NotSupportedException
Catch ex As ArgumentNullException
Catch ex As ArgumentOutOfRangeException
Catch ex As ObjectDisposedException
Catch ex As IO.IOException ’
Catch ex As SocketException
Catch ex As ThreadAbortException
Finally
End Try
End Sub

Private Sub ProcessInfo(ByVal tmpString As String) '对收到的数据进行解析、处理
……
End Sub
……
End Class
结束语
本文着重论述的是在VB2005的环境下,运用多线程异步实现远程DVS数据收集的原vb.net教程理,重点考虑的是怎样提高程序的反应速度,特别讨论了程序开发中的一些细节问题,对有志于从事远程临控系统开发的软件人员有一定的参考意义。
文中代码在windows2003+VB2005+SqlServer2005的环境下调试通过,现在正在使用。

在vb.net中运用多线程实现远程数据收集相关推荐

  1. PyTorch训练中Dataset多线程加载数据,比Dataloader里设置多个workers还要快

    PyTorch训练中Dataset多线程加载数据,而不是在DataLoader 背景与需求 现在做深度学习的越来越多人都有用PyTorch,他容易上手,而且API相对TF友好的不要太多.今天就给大家带 ...

  2. 【PyTorch训练中Dataset多线程加载数据,比Dataloader里设置多个workers还要快】

    文章目录 一.引言 二.背景与需求 三.方法的实现 四.代码与数据测试 五.测试结果 5.1.Max elapse 5.2.Multi Load Max elapse 5.3.Min elapse 5 ...

  3. vb.net中滚动条一直显示没有数据时也显示_Android Studio 中 System Trace 的新增功能...

    Android Studio 中 System Trace 的新增功能 在 Android Studio 4.0 中,我们已经对 CPU Profiler 的 UI 做了大量调整来提供更加直观的工作流 ...

  4. vb.net中滚动条一直显示没有数据时也显示_前端学习中你必须了解的几张图

    1.页面加载事件流程 静态资源加载 2.原型和原型链 3. tcp三次握手和四次挥手 客:第一次握手:我要向你(服)发送请求哦 服:第二次握手:我已经准备好了,你呢? 客:第三次握手:我也准备好了,我 ...

  5. 透彻,在SpringBoot项目中使用Netty实现远程调用

    今日推荐Spring新版本抛弃JVM,可独立部署,网友:要自立门户???国内大神成功给手机装上了 Win11,代码已开源!Fluent Mybatis 牛逼!Nginx 常用配置清单这玩意比Threa ...

  6. C#中的多线程 - 同步基础

    C#中的多线程 - 同步基础 C#中的多线程 - 同步基础 1同步概要 在第 1 部分:基础知识中,我们描述了如何在线程上启动任务.配置线程以及双向传递数据.同时也说明了局部变量对于线程来说是私有的, ...

  7. 使用基于JSON的实体在C#中缓存远程数据

    从GitHub下载最新消息 介绍 除了查询基于JSON / REST的远程服务的简洁方法之外,您通常还需要一种方法来缓存和索引您获取的数据.这对于Web服务尤为重要,因为它们的性质,以及连接服务的延迟 ...

  8. Android中的多线程(字节跳动)

    文章目录 Handler机制(Android中的消息队列机制) Handler机制为Android系统解决了以下两个问题 Handler常用方法 Handler的使用 补充知识点 Handler原理 ...

  9. python3中的多线程

    #!/usr/bin/env python # -*- coding: utf-8 -*- # @Date    : 2018-07-19 21:50:16 # @Author  : cdl (121 ...

  10. iOS 开发中的多线程

    线程.进程 什么是线程.进程   有的人说进程就像是人的脑袋,线程就是脑袋上的头发~~.其实这么比方不算错,但是更简单的来说,用迅雷下载文件,迅雷这个程序就是一个进程,下载的文件就是一个线程,同时下载 ...

最新文章

  1. A.CPP (blur.CPP)如何调用B.CPP (zeros.cpp)中定义的方法
  2. 并发基础(九) java线程的终止与中断
  3. Spring MVC Hibernate验证器使用示例
  4. 前端页面闪烁的问题解决方案
  5. [蓝桥杯][2014年第五届真题]稍大的串(STL)
  6. 自由职业半年,我又滚回职场了...
  7. eclipse创建spring boot项目,tomcat启动成功,但http://localhost:8080无法访问报错404解决方案...
  8. js关于字面量与构造函数创建对象的几点理解
  9. 20172328《程序设计与数据结构》第三周学习总结
  10. 电子商务发展的中心主体与边缘业务
  11. yolov4训练自己的数据 灰度图像_还在为图像训练数据少发愁吗?那是因为你还不会这几招...
  12. 去除右键菜单中图形属性、图形选项
  13. java私塾初级_Java私塾Java初级教程
  14. uni-app项目(一)
  15. 3dmax间隔阵列怎么用
  16. 数学建模竞赛经验分享(从本科生到研究生,获奖成功率100%,我从数模所学)
  17. 干法读后感--磨练灵魂 提升心志
  18. H3C网络流量镜像配置
  19. 更改360极速浏览器默认安装路径
  20. 半导体器件相关专业词汇积累ing

热门文章

  1. 5 分钟,使用内网穿透快速实现远程桌面
  2. 11000-雷达基本资料
  3. QML中使用QSortFilterProxyModel进行排序和过滤
  4. ASP.net开发实践系列视频教程(总共53课) 天轰穿新的视频
  5. 基于Proteus的51单片机仿真
  6. Android 设置网络代理
  7. 罗技K380使用手册
  8. 刚刚,神舟十三号载人飞船即将撤离空间站核心
  9. 微型计算机属于超大规模集成电路计算机,超大规模集成电路计算机是第几代计算机...
  10. Python给指定QQ好友自动发送信息和图片