6

I have a NetworkStream which I read asynchronously (using async/await)

await Task<int>.Factory.FromAsync((cb, state) => stream.BeginRead(buffer, offset, readLen - offset), stream.EndRead, null);

Unfortunatly, an io exception sometimes occurs: "The I/O operation has been aborted because of either a thread exit or an application request."

I believe I hit a requirement documented in Socke.EndReceive: http://msdn.microsoft.com/en-us/library/w7wtt64b.aspx . Which states:

All I/O initiated by a given thread is canceled when that thread exits. A pending asynchronous operation can fail if the thread exits before the operation completes.

Because the async method runs on the default scheduler, this requirement cannot be assured.

Is there a way around this? Do I need to start a dedicated Thread to initiate I/O?

best regards, Dirk

Andrey Agibalov
  • 7,624
  • 8
  • 66
  • 111
Dirk Bonné
  • 648
  • 5
  • 12
  • Are you sure that applies to async I/O? We've used Begin/End on network streams for years in Mindtouch DReAM and it's definitely releasing the initiating threads back to the threadpool all the time. – Arne Claassen Aug 30 '11 at 16:26
  • Hello Arne, that is not the same as freeing up a unmanaged thread. As I understand it, that behaviour is meant as a "help": when the thread terminates, any work associated with it is cancelled. That said, I have trouble reproducing it on my own machine. It happens on a build server where hundreds of tests run, each starting/stopping threads, and appdomains being created and unloaded in fast succession. The SocketException I see is SocketError.OperationAborted on EndReceive. – Dirk Bonné Aug 31 '11 at 08:42
  • Looks like i misread that page. That is indeed unfortunate behavior since the point of async is not to have to worry about the initiating thread. – Arne Claassen Aug 31 '11 at 17:57
  • Not sure I get the problem. If your program's main thread dies, wouldn't you *want* the async operation to fail? – Hans Passant Aug 31 '11 at 21:26
  • @Hans, it is not the main thread that terminates, and no, I do not want the operation to fail. To explain, the asynchronous I/O is initiated by ThreadPool threads and when the result of the I/O arrives a callback is called on a(nother) ThreadPool thread, which in turn can initiate I/O. The idea of the TPL is to abstract the work away from threads, and instead let the supplied scheduler decide where the work should be done. The semantics of Socket.Begin*/End* prevent that freedom, it seems to me. – Dirk Bonné Sep 01 '11 at 07:47
  • I also suffered from this problem...I will now try to restart the request if it fails with OperationAborted and hope that the thread that calls EndReceive will life long enough for the operation to complete... – mmmmmmmm Nov 21 '13 at 13:09

3 Answers3

3

I have asked the same question on the parallelextensions forum:

http://social.msdn.microsoft.com/Forums/en-US/parallelextensions/thread/d116f7b0-c8ee-4ce4-a9b8-4c38120e45a4

Basically, the ThreadPool will not stop any threads that has asynchronous I/O pending. So as long as one starts the socket operation on the ThreadPool this problem should not occur.

Dirk Bonné
  • 648
  • 5
  • 12
1

I would recommend using dedicated I/O threads that never terminate. That is what a lot of real-world code does.

One of the best ways to do it is to have the threads loop around GetQueuedCompletionStatus. If you need to make one of the I/O threads perform an I/O operation, call either PostQueuedCompletionStatus or QueueUserAPC to get the I/O thread to post the operation.

CAUTION: You must handle the case where these operations fail. QueueUserAPC, for example, can fail if too many APCs are already queued.

David Schwartz
  • 179,497
  • 17
  • 214
  • 278
-1

You can use the oldest trick in book. Make your thread sleep in a never ending loop

while(true) {
    Threading.Sleep(TimeSpan.FromSeconds(1); 
}

Note that I have written the above code just from my head. Didn't check it in Visual Studio since I am working on my Mac currently.

Also you will need to write something to exit the thread as well when you need to but that will depend upon how your application is flowing.