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


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


一、线程与委托


1、为什么要用异步?

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

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

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

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


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


2、线程

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


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

线程的开始用Start方法

线程的结束用Abort方法


下面感受一下线程作用:


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

  1. PublicClassForm1
  2. PrivateSubButton1_Click(senderAsObject,eAsEventArgs)HandlesButton1.Click
  3. DimiAsInt32
  4. Fori=0To123451
  5. TextBox1.Text=i
  6. Next
  7. EndSub
  8. PrivateSubButton2_Click(senderAsObject,eAsEventArgs)HandlesButton2.Click
  9. TextBox2.Text="终于出现奇迹"
  10. EndSub
  11. EndClass


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

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

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





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

  1. ImportsSystem.Threading
  2. PublicClassForm1
  3. DimmythreadAsThread
  4. PrivateSubButton1_Click(senderAsObject,eAsEventArgs)HandlesButton1.Click
  5. mythread=NewThread(AddressOfShowNumber)'构造线程
  6. mythread.Name="myShowNumber"
  7. mythread.Start()'启动线程
  8. EndSub
  9. PrivateSubShowNumber()
  10. DimiAsInt32
  11. Fori=0To123451
  12. TextBox1.Text=i
  13. Next
  14. mythread.Abort()'终止线程
  15. EndSub
  16. PrivateSubButton2_Click(senderAsObject,eAsEventArgs)HandlesButton2.Click
  17. TextBox2.Text="终于出现奇迹"
  18. EndSub
  19. EndClass

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

  1. ImportsSystem.Threading
  2. PublicClassForm1
  3. DimmythreadAsThread
  4. PrivateDelegateSubVoidShow(ByRefiAsInt32)'定义要委托的类型
  5. PrivateSubButton1_Click(senderAsObject,eAsEventArgs)HandlesButton1.Click
  6. mythread=NewThread(AddressOfShowNumber)
  7. mythread.Name="myShowNumber"
  8. mythread.Start()
  9. EndSub
  10. PrivateSubShowNumber()
  11. DimiAsInt32
  12. Fori=0To123451
  13. 'TextBox1.Text=i
  14. Me.Invoke(NewVoidShow(AddressOfTureShowNumber),i)'用New构造委托,再用Invoke执行
  15. Next
  16. mythread.Abort()
  17. EndSub
  18. '新加入的被委托要做的事
  19. PrivateSubTureShowNumber(ByRefiAsInt32)
  20. TextBox1.Text=i
  21. EndSub
  22. PrivateSubButton2_Click(senderAsObject,eAsEventArgs)HandlesButton2.Click
  23. TextBox2.Text="终于出现奇迹"
  24. EndSub
  25. EndClass


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

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



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

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

这个不影响使用。






二、异步读写


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

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

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

即流程如下:


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

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

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


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

  1. PublicSubNew(_
  2. pathAsString,_
  3. modeAsFileMode,_
  4. accessAsFileAccess,_
  5. shareAsFileShare,_
  6. bufferSizeAsInteger,_'缓冲大小
  7. useAsyncAsBoolean_'True为异步
  8. )


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

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

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

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



  1. ImportsSystem.IO
  2. ImportsSystem.Threading
  3. PublicClassForm1
  4. DimbtArray(15)AsByte
  5. DimfsAsFileStream
  6. DimmyThreadAsThread
  7. DimblnProcessAsBoolean'进程是否使用标志
  8. PrivateDelegateSubShowMyMessage(ByValstrAsString)'线程中无法调用窗体控件,用委托解决
  9. '启动写或读进程
  10. PrivateSubButton1_Click(senderAsObject,eAsEventArgs)HandlesButton1.Click
  11. TextBox1.Text=""
  12. Try
  13. IfRadioButton2.Checked=TrueThen'写选中
  14. myThread=NewThread(AddressOfWriteData)
  15. myThread.Name="WriteBulkData"
  16. myThread.Start()
  17. Else
  18. myThread=NewThread(AddressOfReadData)
  19. myThread.Name="ReadBulkData"
  20. myThread.Start()
  21. EndIf
  22. CatchexAsException
  23. MessageBox.Show(ex.Message)
  24. EndTry
  25. EndSub
  26. PrivateSubWriteData()
  27. Try
  28. fs=NewFileStream("D:\11.txt",FileMode.Open,FileAccess.Write,FileShare.Write,16,True)
  29. DimmyWCBAsNewAsyncCallback(AddressOfMyAsyncWriteCallBack)
  30. blnProcess=True
  31. fs.BeginWrite(btArray,12,myWCB,nothing)
  32. ProcessMessage("Write")
  33. fs.Close()
  34. CatchexAsException
  35. MessageBox.Show(ex.Message)
  36. EndTry
  37. EndSub
  38. PrivateSubReadData()
  39. Try
  40. fs=NewFileStream("d:\11.txt",FileAccess.Read,FileShare.Read,True)
  41. DimmyRCBAsNewAsyncCallback(AddressOfMyAsyncReadCallBack)
  42. blnProcess=True
  43. fs.BeginRead(btArray,myRCB,nothing)
  44. ProcessMessage("Read")
  45. fs.Close()
  46. CatchexAsException
  47. MessageBox.Show(ex.Message)
  48. EndTry
  49. EndSub
  50. PrivateSubMyAsyncWriteCallBack(ByValmyIarAsIAsyncResult)
  51. Thread.Sleep(50)
  52. blnProcess=False
  53. fs.EndWrite(myIar)
  54. '委托显示信息
  55. DimstrAsString="异步线程数据写入完成。"
  56. Me.Invoke(NewShowMyMessage(AddressOfShowMessage),str)
  57. EndSub
  58. PrivateSubMyAsyncReadCallBack(ByValmyIarAsIAsyncResult)
  59. Thread.Sleep(50)
  60. blnProcess=False
  61. fs.EndRead(myIar)
  62. '委托显示信息
  63. DimstrAsString="异步线程数据读取完成。"
  64. Me.Invoke(NewShowMyMessage(AddressOfShowMessage),str)
  65. EndSub
  66. PrivateSubShowMessage(ByValstrAsString)
  67. TextBox1.Text&=Now.ToString&str&vbCrLf
  68. EndSub
  69. PrivateSubProcessMessage(ByValstrRWAsString)
  70. DimstrMessageAsString=""
  71. IfstrRW="Read"Then
  72. strMessage="判断异步正在读取..."
  73. Else
  74. strMessage="判断异步正在写入..."
  75. EndIf
  76. DoWhileblnProcess=True
  77. Me.Invoke(NewShowMyMessage(AddressOfShowMessage),strMessage)
  78. Loop
  79. Thread.Sleep(50)
  80. strMessage="判断读写已经完成。"
  81. Me.Invoke(NewShowMyMessage(AddressOfShowMessage),strMessage)
  82. EndSub
  83. EndClass


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

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

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

相关文章

Format[$] ( expr [ , fmt ] ) format 返回变体型 format$ 强...
VB6或者ASP 格式化时间为 MM/dd/yyyy 格式,竟然没有好的办...
在项目中添加如下代码:新建窗口来显示异常信息。 Namespace...
转了这一篇文章,原来一直想用C#做k3的插件开发,vb没有C#用...
Sub 分列() ‘以空格为分隔符,连续空格只算1个。对所选...
  窗体代码 1 Private Sub Text1_OLEDragDrop(Data As Dat...