tags:

views:

137

answers:

4

Can I safely close a System.IO.Stream (or any of the wrappers or Readers) from a second thread?

I've a new System.IO.StreamReader(inputStream, Encoding.ASCII); reading from network, and am considering shutting it down from another thread by closing it.

If this is not a good idea then what's another way of forcing the thread blocking in a read call from returning?

+1  A: 

Yes, if inputStream is reading from a socket, then you can dispose on another thread which will close the socket connection. You should be aware that the blocking Read call will throw an exception.

Mark Heath
+2  A: 

No it is not safe to blindly call the Close method from a separate thread. The Stream class is not listed as thread safe and calling Close from another thread while the original thread is using it will result in undefined behavior.

JaredPar
+1  A: 

A common approach to clean up tidily in this situation is:

  1. Set a value that indicates completion (eg IsDone = true)
  2. Write some data to the socket so that the blocking read gets some data
  3. On the thread that is reading from the socket, check if IsDone is true before processing the read data. If IsDone is true, ignore the data, and close the stream.

This should avoid any of the issues Jared is referencing.

Nader Shirazie
surely you can only do step 2 if you happen to be in control of both ends of the socket connection, which is rare.
Mark Heath
@Mark: Right, well, actually this assumes you can write to the stream from the same side as you are listening on... I might be out to lunch on that one...
Nader Shirazie
A: 

Yes, it's fine to close a stream on another thread but be aware of the consequences of doing so. If you do and another thread is using that stream, you'll get an exception. The correct thing to do is to have either an event or a wait handle that the thread doing the reading can check to see if it should shut down. The pseudocode would look something like this: Thread 1. pend a read operation and see if there's data to be read.
if ther's data, read some data. if not, continue check the wait handle. If it's set, close it and terminate otherwise read some more loop

Thread 2. If I should trigger the network to disconnect, signal the wait handle else do whatever.

Be aware that if you're on a blocking operation, you'll get an exception. I recommend NEVER using a blocking socket, there's no reason to. We actually do (almost) all of our operations async under the hood of System.Net and the sync code paths just trigger the async code path and then block until they complete.

Jeff Tucker
Jeff: It would be interesting to see a code example of this. leeeroy is using a StreamReader -- it doesn't have any async methods on it. Does that mean that he would need to read from the network stream directly, and then "pump" the read values into either a text encoder?
JMarsch
StreamReader.Read doesn't block so there's no need for async methods (it returns -1 if no more data exists). What you'd do is something like this:while(someCondition){if(handle.WaitOne(1)){//time to dispose of the streamstream.Close();stream.Dispose();myStreamDisposalCompleteHandle.Set()}else{ // stream is still fine to usedata = stream.Read() // this is non-blocking}In the other thread:if(appShouldCloseStreams){foreach(streamShouldCloseHandle handle in HandlesThatStreamsHave)handle.Set();WaitHandle.WaitAll(arayOfAllStreamDisposeCompleteHandles);App.Exit();
Jeff Tucker
Sorry about the formatting above. What is going on in that code is that each thread handling a stream has two handles: one that it periodically checks to see if it should close, and one that it can use to signal that it's closed the stream. The main thread has a reference to all of the handles that all of the stream threads have. When it's ready to signal that it's time to end, it signals all the other threads and then waits for them to signal that they are finished. Use the ManualResetEvent or AutoResetEvent classes for your handles, WaitHandle is the base class but it has no Set method.
Jeff Tucker
StreamReader.Read will block if the underlying stream blocks.
nos