I'm running into a problem with TPL dataflow that I can't seem to figure out. My code will run anywhere from 15 minutes to a couple of hours before it just deadlocks. I've done as much digging as I can and I will try to post it here.
Also, I've read that you generally shouldn't mix asynchronous and blocking code. If I'm doing it wrong by isolating my TPL Dataflow code to a Task.Run(Func<Task<bool>>)
call, let me know. Otherwise, I don't think this is the problem since it's running on its own thread.
My dataflow network is set up pretty much exactly as in this SO link, please refer to it for the code.
The TransformBlock
is pretty involved with lots of various await
s. One of the functions calls await httpRequest.GetRequestStreamAsync()
. This seems to be where the deadlock is occurring, because running a memory profiler reveals four compiler-generated IAsyncStateMachine
structs stuck in memory (e.g. <ExampleAsync>d__0
) that all represent a state immediately before it's called.
It also shows that 110 objects are stuck waiting to be processed in the TransformManyBlock
(and by the next BatchedJoinBlock
), and there are only 267 HTTP connections open (ServicePointManager.DefaultConnectionLimit
is set to 512).
Checking the source code of the GetRequestStreamAsync
function reveals that at least another thread is created with Task.Run<>()
, which seems unnecessary to me. I'm thinking I could even just call the FromAsync
function myself:
if (...)
{
return Task.Run<Stream>(() => {
TaskFactory<Stream> factory = Task<Stream>.Factory;
WebRequest webRequest = this;
WebRequest webRequest1 = this;
return factory.FromAsync(
new Func<AsyncCallback, object, IAsyncResult> (webRequest.BeginGetRequestStream),
new Func<IAsyncResult, Stream>(webRequest1.EndGetRequestStream),
null
);
});
}
Could it be that the thread pool is becoming exhausted?
My only other clue is that the BatchedJoinBlock
s may be hogging resources, because even after each one is completed, they never get disposed -- which leads me to want to continue the BatchedJoinBlock.Completion
task with one that will unlink itself upon completion. (Setting MaxNumberOfGroups
to 1 didn't seem to do that.) I believe there were nearly 4000 lingering BatchedJoinBlock
s.
I'm stumped. If something is timing out, an exception is caught and passed to the Exception
target of the BatchedJoinBlock
. Obviously, this isn't the case.