View Single Post
 
Old 10-31-2004, 08:00 AM
excaliber's Avatar
excaliber excaliber is offline
Senior Contributor

* Expert *
 
Join Date: Nov 2002
Location: Ohio, USA
Posts: 1,828
Default

Code
Finally! Lets hit some code. By now, if you are still reading, you might have gone into shock. There were some pretty gory details above, but fear not! Asynch programming is absurdly simple in .Net.

Code:
Private Sub ReadFilesInC() ' make a reference to a directory Dim di As New IO.DirectoryInfo("c:\") Dim diar1 As IO.FileInfo() = di.GetFiles() Dim dra As IO.FileInfo 'list the names of all files in the specified directory For Each dra In diar1 ReadData(dra.FullName) Next End Sub Public Function ReadData(ByVal Path As String) '--This is the state object that will contain the file '---and the return data. This class is nescessary because '---we dont want the return data overwritten by each '---read method (like it would if it was declared globally) Dim tempStateObj As New StateObj '--Open up the file we wish to read tempStateObj.file = New System.IO.FileStream(Path, IO.FileMode.Open) '--Redimension the return data array to the appropriate length ReDim tempStateObj.retData(tempStateObj.file.Length) '--Call the Asynch read method. Pass the byte array we wish to fill '---the offset, the length of the file to read, the address of the '---callback sub, and finally a state object for our own use. tempStateObj.file.BeginRead(tempStateObj.retData, 0, tempStateObj.file.Length, _ New AsyncCallback(AddressOf OnReadDone), tempStateObj) '--Control is immediately given back to the program. The BeginRead method '---Does not block program execution like the Read method, so we are free to '---get back to processing. Debug.WriteLine("BeginRead done: " & Path) '--To show that this is a normal thread and not threadpooled: Debug.WriteLine(System.Threading.Thread.CurrentThread.IsThreadPoolThread) End Function Public Sub OnReadDone(ByVal ar As IAsyncResult) '--This is the asynchronous callback delegate that is called when the BeginRead '---is done processing. '--The state object is passed to us in ar. It is a generic '---IAsyncResult, and must be casted into something usable '---We know we passed a StateObj class, so we cast it as such Dim state As StateObj = CType(ar, StateObj) '--From our state object, we must call EndRead(ar) to get the '---number of bytes read. Even if you do not wish to know the '---number of bytes, you must *always* call EndRead in the callback Dim bytesRecieved As Int32 = state.file.EndRead(ar) '--If you don't wish to know the number of bytes read, do this: 'state.file.EndRead(ar) state.file.Close() '--Just to prove that this thread is running in a Threadpool object that '---is managed by the OS: Debug.WriteLine(System.Threading.Thread.CurrentThread.IsThreadPoolThread) '--Open up a new file to write to state.file = New System.IO.FileStream("C:\Somewhere\data.txt", IO.FileMode.Create) '--Begin the Asynch write to the file, passing everything and the state object again state.file.BeginWrite(state.retData, 0, state.retData.Length, New AsyncCallback(AddressOf OnWriteDone), state) '--At this point, the sub will terminate and the thread will go back to the '---internal threadpool. It is important not to do anything that will block or take '---large amounts of time in this sub. The internal threadpool is limited to '---25 threads total, and it is important to return the thread as quick as possible '---to the pool for further use. End Sub Public Sub OnWriteDone(ByVal ar As IAsyncResult) '--This is the asynchronous callback delegate that is called when the BeginWrite '---is done processing. '--The state object is passed to us in ar. It is a generic '---IAsyncResult, and must be casted into something usable '---We know we passed a StateObj class, so we cast it as such Dim state As StateObj = CType(ar, StateObj) '--From our state object, we must call EndWrite(ar). You must '---*always* call EndWrite in the callback state.file.EndWrite(ar) state.file.Close() '--At this point, the sub will terminate and the thread will go back to the '---internal threadpool. It is important not to do anything that will block or take '---large amounts of time in this sub. The internal threadpool is limited to '---25 threads total, and it is important to return the thread as quick as possible '---to the pool for further use. End Sub '---------- '--Our StateObj Class '---------- Public Class StateObj Public file As System.IO.FileStream Public retData() As Byte End Class

Hopefully the comments are fairly self explanatory. At first, we call OpenFilesInC() to start reading ever file in the C:\ Drive (but not subdirectories). This will then start calling ReadData(), which will asynchrounously start opening and reading them. The ReadData will return immediately after executing, unlike a similar function that uses Read() instead of BeginRead().

ReadData() uses a small public class called StateObj. This class contains the file and the return data array. This is important. Each file that you open asynchronously must have it's own buffer to store data in. If you used a global variable, each file would overwrite the others, making quite a mess. Luckily, the asynch methods allow us to pass a state object that can be retrieved at the callback.

The callback delegate itself is also fairly self explanatory. We cast the IAsynResult object to our original StateObj class, then call EndRead(ar) to get the number of bytes read. Regardless of if you use the number of bytes or choose to store it, you must always call EndRead() in the callback. If you don't, bad things (TM) could happen.

The same procedure is used for writing. This is one of the great features of the .Net platform. The sockets have the same naming and usage as the file io, which has the same as memory streams, etc. YOu can take your knowledge from this and apply it to many other objects. Any method that has the prefix Begin... on it (BeginRead, BeginWrite, BeginAccept, etc) will have a corresponding End... method (EndRead, EndWrite, EndAccept, etc). These two pairs form the asynch methods in any object that has them.

Ending
This was just a brief glance at asynch programming. It is a great tool that can drastically improve performance in many situations. It does raise some interesting programming hurdles and make some things more complicated (such as requiring state objects and such), but it is well worth it. Just remember, always call your End... method

Note on example
The above example would probably be quite a memory hog, as it is reading the entire file into memory at once. A better solution would be to use a smaller buffer (of perhaps 4-10 KB) and read that, then call the BeginWrite, then call another BeginRead to get the next block of data from the file, etc.

As to the demo, it does exactly what the code posted does (with some modifications). Reading the article gives an overview of what the code does. It reads all files from the location passed to the ReadFiles function (specified by the text box txtInput) and asynchronously reads them, the asynchronously writes them to the application directory. It is *not* recursive, so no fear of copying your entire computer to the directory.
Attached Files
File Type: zip AsynchFile.zip (6.5 KB, 157 views)

Last edited by lebb; 11-14-2004 at 09:44 AM.
Reply With Quote