笔记7:vb.net的异步读写数据流使用线程、委托

在理解异步读写前,了解一下线程和委托是必要的。


一、线程与委托


1、为什么要用异步?

无论是MemoryStream,BufferedStream,FileStream数据流,一旦的读写开始,应用程序就会处于停滞状况。

直到数据读写完成,并返回。

文件数据的读写基本上是一种非常消耗资源的过程,处理的数据量越大,I/O对系统性能的影响就越明显。

为了避免长时间等待I/O操作使程序处理“瘫痪”状态,异步I/O就显得非常重要。


异步的实现就是使用一个新的线程来完成,主线程的任务并不影响,这样大大提高了程序的效能。


2、线程

每个程序有一个主线程,如果一个循环处于主线程中,程序在较长的循环,将出现“不响应”的情况。


线程在System.Threading中。线程创建可专用于一个功能块(方法、函数),

线程的开始用Start方法

线程的结束用Abort方法


下面感受一下线程作用:


窗体上添加两Button,两个TextBox,代码如下,点击Button1启动循环,接着点击Button2.

Public Class Form1

    Private Sub Button1_Click(sender As Object,e As EventArgs) Handles Button1.Click
        Dim i As Int32
        For i = 0 To 123451
            TextBox1.Text = i
        Next
    End Sub

    Private Sub Button2_Click(sender As Object,e As EventArgs) Handles Button2.Click
        TextBox2.Text = "终于出现奇迹"
    End Sub
End Class


可以明显看到虽然点击了Button1,但TextBox1的内容并没有什么变化,同时,在点击Button2时,TextBox并没有内容显示。

这是因为线程正被循环一直占用,暂时无法响应Button2,直到循环完成后,它才终于忙过来处理Button2.

这会给用户造成“程序已经无响应、死了”的误会。





下面改善上面的做法,新建一个线程来专门处理循环,这样就不影响主线程响应Button2:

Imports System.Threading
Public Class Form1
    Dim mythread As Thread

    Private Sub Button1_Click(sender As Object,e As EventArgs) Handles Button1.Click
        mythread = New Thread(AddressOf ShowNumber) '构造线程
        mythread.Name = "myShowNumber"
        mythread.Start() '启动线程
    End Sub

    Private Sub ShowNumber()
        Dim i As Int32
        For i = 0 To 123451
            TextBox1.Text = i
        Next

        mythread.Abort() '终止线程
    End Sub

    Private Sub Button2_Click(sender As Object,e As EventArgs) Handles Button2.Click
        TextBox2.Text = "终于出现奇迹"
    End Sub
End Class

然而一点击,发现出错,提示:线程间操作无效: 从不是创建控件“TextBox1”的线程访问它。


这是因为Textbox1是主线程中的,却在另一个新的线程中访问,这种是不安全的,相当于去别人房间使用电视机。

怎么办?这里可以用委托,委托能够进别人房间的人去使用电视机。



3、委托

委托的思想,就是自己不能干或不想干的事,委托另一个有能力或有权限的人去干那件事。

实际上,我们一直要用委托思想,比如基本类型的变量名。Dim i As Integer

i变量名就是相当于委托,实际上,一个变量代表的是指定内存地址中的值,如果不用变量名,就得实际上引用这个内存的地址。

而我们就用“变量名”来干操作这个地址里的东西。


除了变量名可以用委托一样,方法也可以用委托,这就是我们普通所说的委托。

定义和使用大致与变量名的方式一样:

(1)定义委托类型: Private Delegate Sub MyDelegate(byval k as int32) '参数多种,多个)

这里类似定义变量的类型一样。

(2)定义要赋的具体“值”: 这里的具体值,不是值,而是一个具体的方法,方法的形式必须与上面定义保持一致。就象变量名是整形时,赋值也应该是整形,而不是String.

例如:Private Sub YourSelfMethod(byval m as int32) '方法名自定,但形式与(1)保持一致。

(3)调用这个值: 也就是委托去办事。用Invoke方法:Control.Invoke(New MyDelegate(AddressOf YourSelfMethod),intValue)

这一步就把(1),(2)使用上了。


下面接着上面的例子,使用委托来调用Form1中的TextBox1.

Imports System.Threading
Public Class Form1
    Dim mythread As Thread
    Private Delegate Sub VoidShow(ByRef i As Int32) '定义要委托的类型

    Private Sub Button1_Click(sender As Object,e As EventArgs) Handles Button1.Click
        mythread = New Thread(AddressOf ShowNumber)
        mythread.Name = "myShowNumber"
        mythread.Start()
    End Sub

    Private Sub ShowNumber()
        Dim i As Int32
        For i = 0 To 123451
            'TextBox1.Text = i
            Me.Invoke(New VoidShow(AddressOf TureShowNumber),i) '用New构造委托,再用Invoke执行
        Next

        mythread.Abort()
    End Sub

    '新加入的被委托要做的事
    Private Sub TureShowNumber(ByRef i As Int32)
        TextBox1.Text = i
    End Sub


    Private Sub Button2_Click(sender As Object,e As EventArgs) Handles Button2.Click
        TextBox2.Text = "终于出现奇迹"
    End Sub
End Class


点击Buttton1,可以看到因为新线程的使用,TextBox1中的数字一直在变量。

而且,同时点击Button2程序不会“死机”,很快地响应。



注意的是:因为线程的中止使用的是强制中断Abort,所以即时窗体会显示:

System.Threading.ThreadAbortException 类型的第一次机会异常在 mscorlib.dll中发生

这个不影响使用。






二、异步读写


异步I/O与同步I/O最大的不同在于: 同步I/O只有完成整个I/O操作后,程序才会进行下一步(所以这之前象死机一样)。

异步I/O在操作读写操作的同时,程序可以继续下一步工作,不影响程序其它执行。

简单地说,主线程和新线程各自执行,不相互影响。

即流程如下:


程序(主线程)在左边开始时,就建立了新线程进行异步读写。

在异步开始时,就传入了一个回调参数,这个用于异步完成时,自动调用这个参数所指的过程。

其中的IAsyncResult表示异步操作的状态。结束异步操作时需要这个参数。


一般我们在I/O操作时都是同步,异步在FileStream构造时就必须指明文件采用的异步方法:

Public Sub New ( _
	path As String,_
	mode As FileMode,_
	access As FileAccess,_
	share As FileShare,_
	bufferSize As Integer,_  '缓冲大小
	useAsync As Boolean _    'True为异步
)


下面看一下异步操作的例子:

1、委托:只是为了在线程中调用窗体中的控件TextBox1来显示状态。

2、线程:是异步I/O的必要过程

3、回调函数:这是异步完成后,自动来通知或告之,异步I/O已经完成了(否则,怎么知道异步的结束呢?)



Imports System.IO
Imports System.Threading
Public Class Form1
    Dim btArray(15) As Byte
    Dim fs As FileStream
    Dim myThread As Thread
    Dim blnProcess As Boolean  '进程是否使用标志

    Private Delegate Sub ShowMyMessage(ByVal str As String) '线程中无法调用窗体控件,用委托解决

    '启动写或读进程
    Private Sub Button1_Click(sender As Object,e As EventArgs) Handles Button1.Click
        TextBox1.Text = ""
        Try
            If RadioButton2.Checked = True Then '写选中
                myThread = New Thread(AddressOf WriteData)
                myThread.Name = "WriteBulkData"
                myThread.Start()
            Else
                myThread = New Thread(AddressOf ReadData)
                myThread.Name = "ReadBulkData"
                myThread.Start()
            End If
        Catch ex As Exception
            MessageBox.Show(ex.Message)
        End Try
    End Sub

    Private Sub WriteData()
        Try
            fs = New FileStream("D:\11.txt",FileMode.Open,FileAccess.Write,FileShare.Write,16,True)
            Dim myWCB As New AsyncCallback(AddressOf MyAsyncWriteCallBack)
            blnProcess = True
            fs.BeginWrite(btArray,12,myWCB,Nothing)
            ProcessMessage("Write")
            fs.Close()
        Catch ex As Exception
            MessageBox.Show(ex.Message)
        End Try
    End Sub

    Private Sub ReadData()
        Try
            fs = New FileStream("d:\11.txt",FileAccess.Read,FileShare.Read,True)
            Dim myRCB As New AsyncCallback(AddressOf MyAsyncReadCallBack)
            blnProcess = True
            fs.BeginRead(btArray,myRCB,Nothing)
            ProcessMessage("Read")
            fs.Close()
        Catch ex As Exception
            MessageBox.Show(ex.Message)
        End Try
    End Sub

    Private Sub MyAsyncWriteCallBack(ByVal myIar As IAsyncResult)
        Thread.Sleep(50)
        blnProcess = False
        fs.EndWrite(myIar)

        '委托显示信息
        Dim str As String = "  异步线程数据写入完成。"
        Me.Invoke(New ShowMyMessage(AddressOf ShowMessage),str)
    End Sub

    Private Sub MyAsyncReadCallBack(ByVal myIar As IAsyncResult)
        Thread.Sleep(50)
        blnProcess = False
        fs.EndRead(myIar)

        '委托显示信息
        Dim str As String = "  异步线程数据读取完成。"
        Me.Invoke(New ShowMyMessage(AddressOf ShowMessage),str)
    End Sub

    Private Sub ShowMessage(ByVal str As String)
        TextBox1.Text &= Now.ToString & str & vbCrLf
    End Sub


    Private Sub ProcessMessage(ByVal strRW As String)
        Dim strMessage As String = ""
        If strRW = "Read" Then
            strMessage = "  判断异步正在读取..."
        Else
            strMessage = "  判断异步正在写入..."
        End If

        Do While blnProcess = True
            Me.Invoke(New ShowMyMessage(AddressOf ShowMessage),strMessage)
        Loop
        Thread.Sleep(50)

        strMessage = "  判断读写已经完成。"
        Me.Invoke(New ShowMyMessage(AddressOf ShowMessage),strMessage)
    End Sub
End Class


上面通过一个循环不断判断异步进行得怎么样(实际上是用的全局blnProcess来判断)

因为是例子,数据量不大,所以在过程加加入Sleep来延迟异步还在进行中。

为了减少显示的信息,把时间延时量减小到50毫秒。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


Format[$] ( expr [ , fmt ] ) format 返回变体型 format$ 强制返回为文本 -------------------------------- 数字类型的格式化 --------------------------------     固定格式参数:     General Number 普通数字,如可以用来去掉千位分隔号     format$("100,1
VB6或者ASP 格式化时间为 MM/dd/yyyy 格式,竟然没有好的办法, Format 或者FormatDateTime 竟然结果和系统设置的区域语言的日期和时间格式相关。意思是尽管你用诸如 Format(Now, "MM/dd/yyyy"),如果系统的设置格式区域语言的日期和时间格式分隔符是"-",那他还会显示为 MM-dd-yyyy     只有拼凑: <%response.write
在项目中添加如下代码:新建窗口来显示异常信息。 Namespace My ‘全局错误处理,新的解决方案直接添加本ApplicationEvents.vb 到工程即可 ‘添加后还需要一个From用来显示错误。如果到这步还不会则需要先打好基础啦 ‘======================================================== ‘以下事件
转了这一篇文章,原来一直想用C#做k3的插件开发,vb没有C#用的爽呀,这篇文章写与2011年,看来我以前没有认真去找这个方法呀。 https://blog.csdn.net/chzjxgd/article/details/6176325 金蝶K3 BOS的插件官方是用VB6编写的,如果  能用.Net下的语言工具开发BOS插件是一件很愉快的事情,其中缘由不言而喻,而本文则是个人首创,实现在了用V
Sub 分列() ‘以空格为分隔符,连续空格只算1个。对所选中的单元格进行处理 Dim m As Range, tmpStr As String, s As String Dim x As Integer, y As Integer, subStr As String If MsgBox("确定要分列处理吗?请确定分列的数据会覆盖它后面的单元格!", _
  窗体代码 1 Private Sub Text1_OLEDragDrop(Data As DataObject, Effect As Long, Button As Integer, Shift As Integer, X As Single, Y As Single) 2 Dim path As String, hash As String 3 For Each fil
  Imports MySql.Data.MySqlClient Public Class Form1 ‘ GLOBAL DECLARATIONS Dim conString As String = "Server=localhost;Database=net2;Uid=root;Pwd=123456;" Dim con As New MySqlConnection
‘導入命名空間 Imports ADODB Imports Microsoft.Office.Interop   Private Sub A1() Dim Sql As String Dim Cnn As New ADODB.Connection Dim Rs As New ADODB.Recordset Dim S As String   S = "Provider=OraOLEDB.Oracl
Imports System.IO Imports System.Threading Imports System.Diagnostics Public Class Form1 Dim A(254) As String    Function ping(ByVal IP As Integer) As String Dim IPAddress As String IPAddress = "10.0.
VB运行EXE程序,并等待其运行结束 参考:https://blog.csdn.net/useway/article/details/5494084 Private Declare Function WaitForSingleObject Lib "kernel32" (ByVal hHandle As Long, ByVal dwMilliseconds As Long) As Long Pr
今天碰到一个问题,登陆的时候,如果不需要验证手机号为空,则不去验证手机号 因为登陆的时候所有的验证信息都存放在一个数组里 Dim CheckUserInfo() As String ={UserBirthday, SecEmail, UserMob, UserSex, RealNameFirst, RealName, CheckCardID, CheckCardType, Contactemail
在VB6.0中,数据访问接口有三种: 1、ActiveX数据对象(ADO) 2、远程数据对象(RDO) 3、数据访问对象(DAO) 1.使用ADO(ActiveX Data Objec,ActiveX数据对象)连接SQL Server 1)使用ADO控件连接 使用ADO控件的ConnectionString属性就可以连接SQL Server,该属性包含一个由分号分隔的argument=value语
注:大家如果没有VB6.0的安装文件,可自行百度一下下载,一般文件大小在200M左右的均为完整版的软件,可以使用。   特别提示:安装此软件的时候最好退出360杀毒软件(包括360安全卫士,电脑管家等,如果电脑上有这些软件的话),因为现如今的360杀毒软件直接会对VB6.0软件误报,这样的话就可能会在安装过程中被误报阻止而导致安装失败,或者是安装后缺乏很多必须的组件(其它的杀毒软件或安全卫士之类的
Private Sub Form_Load() Call conndb End Sub Private Function conndb() Dim cn As New ADODB.Connection Dim rs As New ADODB.Recordset Dim strCn, sql As String Dim db_host As String Dim db_user As String
  PPSM06S70:  Add  moddate  EDITSPRINTJOB:  MAX(TO_CHAR(ETRN.MODDATE, ‘yyyy/mm/dd/HH24:MI AM‘)) ACTUAL_SHIPDATE   4.Test Scenario (1) :Query SQL Test DN:8016578337 SELECT CTRN.TKCTID TRUCK_ID,        
  沒有出現CrystalReportViewer時,須安裝CRforVS_13_0. 新增1個數據集,新增1個數據表,添加二列,列名要和資料庫名一樣. 修改目標Framework 修改app.config, <startup >改成<startup useLegacyV2RuntimeActivationPolicy ="true">  CrystalReport1.rpt增加數據庫專家 在表單
Imports System.Threading Imports System Public Class Form1 Dim th1, th2 As Thread Public Sub Method1() Dim i As Integer For i = 1 To 100 If Me.Label1.BackColor =
Friend Const PROCESS_ALL_ACCESS = &H1F0FFF = 2035711 Friend Const PROCESS_VM_READ = &H10 Friend Const PROCESS_VM_WRITE = &H20 Friend Const PAGE_READONLY = &H2 Friend Const PAGE_READWRITE = &H4 Friend
以下代码随手写的 并没有大量测试 效率也有待提升 如果需要C#的请自行转换 Function SplitBytes(Data As Byte(), Delimiter As Byte()) As List(Of Byte()) Dim i = 0 Dim List As New List(Of Byte()) Dim bytes As New
Imports System.Data.SqlClient Public Class Form1 REM Public conn1 As SqlConnection = New SqlConnection("server=.; Integrated Security=False;Initial Catalog= mydatabase1; User ID= sa;password")