VB.net 2010 视频教程 VB.net 2010 视频教程 python基础视频教程
SQL Server 2008 视频教程 c#入门经典教程 Visual Basic从门到精通视频教程
当前位置:
首页 > VB.net教程 >
  • 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


  1.  
          然而一点击,发现出错,提示:线程间操作无效: 从不是创建控件“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



  1.  
             

            点击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, 0, 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", FileMode.Open, FileAccess.Read, FileShare.Read, 16, True)

        Dim myRCB As New AsyncCallback(AddressOf MyAsyncReadCallBack)

        blnProcess = True

        fs.BeginRead(btArray, 0, 16, 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


  1.  
              

 

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

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

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

 

 

 


相关教程