在理解异步读写前,了解一下线程和委托是必要的。
一、线程与委托
?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,?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
上面通过一个循环不断判断异步进行得怎么样(实际上是用的全局blnProcess来判断)
因为是例子,数据量不大,所以在过程加加入Sleep来延迟异步还在进行中。
为了减少显示的信息,把时间延时量减小到50毫秒。转载请注明出处:
未经允许不得转载:lxfamn » vb.net的异步线程、委托 读写数据流(使用线程、委托)