c# - Why does cancellation block for so long when cancelling a lot of HTTP requests? -



c# - Why does cancellation block for so long when cancelling a lot of HTTP requests? -

background

i have code performs batch html page processing using content 1 specific host. tries create big number (~400) of simultaneous http requests using httpclient. believe maximum number of simultaneous connections restricted servicepointmanager.defaultconnectionlimit, i'm not applying own concurrency restrictions.

after sending of requests asynchronously httpclient using task.whenall, entire batch operation can cancelled using cancellationtokensource , cancellationtoken. progress of operation viewable via user interface, , button can clicked perform cancellation.

problem

the phone call cancellationtokensource.cancel() blocks 5 - 30 seconds. causes user interface freeze. suspect occurs because method calling code registered cancellation notification.

what i've considered limiting number of simultaneous http request tasks. consider work-around because httpclient seems queue excess requests itself. performing cancellationtokensource.cancel() method phone call in non-ui thread. didn't work well; task didn't run until of others had finished. think async version of method work well, couldn't find one. also, have impression it's suitable utilize method in ui thread. demonstration code class programme { private const int desirednumberofconnections = 418; static void main(string[] args) { manyhttprequeststest().wait(); console.writeline("finished."); console.readkey(); } private static async task manyhttprequeststest() { using (var client = new httpclient()) using (var cancellationtokensource = new cancellationtokensource()) { var requestscompleted = 0; using (var allrequestsstarted = new countdownevent(desirednumberofconnections)) { action reportrequeststarted = () => allrequestsstarted.signal(); action reportrequestcompleted = () => interlocked.increment(ref requestscompleted); func<int, task> gethttpresponse = index => gethttpresponse(client, cancellationtokensource.token, reportrequeststarted, reportrequestcompleted); var httprequesttasks = enumerable.range(0, desirednumberofconnections).select(gethttpresponse); console.writeline("http requests batch beingness initiated"); var httprequeststask = task.whenall(httprequesttasks); console.writeline("starting {0} requests (simultaneous connection limit of {1})", desirednumberofconnections, servicepointmanager.defaultconnectionlimit); allrequestsstarted.wait(); cancel(cancellationtokensource); await waitforrequeststofinish(httprequeststask); } console.writeline("{0} http requests completed", requestscompleted); } } private static void cancel(cancellationtokensource cancellationtokensource) { console.write("cancelling..."); var stopwatch = stopwatch.startnew(); cancellationtokensource.cancel(); stopwatch.stop(); console.writeline("took {0} seconds", stopwatch.elapsed.totalseconds); } private static async task waitforrequeststofinish(task httprequeststask) { console.writeline("waiting http requests finish"); seek { await httprequeststask; } grab (operationcanceledexception) { console.writeline("http requests cancelled"); } } private static async task gethttpresponse(httpclient client, cancellationtoken cancellationtoken, action reportstarted, action reportfinished) { var getresponse = client.getasync("http://www.google.com", cancellationtoken); reportstarted(); using (var response = await getresponse) response.ensuresuccessstatuscode(); reportfinished(); } } output

why cancellation block long? also, there i'm doing wrong or doing better?

performing cancellationtokensource.cancel() method phone call in non-ui thread. didn't work well; task didn't run until of others had finished.

what tells me you're suffering 'threadpool exhaustion', threadpool queue has many items in (from http requests completing) takes while through them all. cancellation blocking on threadpool work item executing , can't skip head of queue.

this suggests need go alternative 1 consideration list. throttle own work threadpool queue remains relatively short. app responsiveness overall anyway.

my favorite way throttle async work utilize dataflow. this:

var block = new actionblock<uri>( async uri => { var httpclient = new httpclient(); // httpclient isn't thread-safe, protect against concurrency using dedicated instance each request. var result = await httpclient.getasync(uri); // more stuff result. }, new executiondataflowblockoptions { maxdegreeofparallelism = 20, cancellationtoken = cancellationtoken }); (int = 0; < 1000; i++) block.post(new uri("http://www.server.com/req" + i)); block.complete(); await block.completion; // waits until done or canceled.

as alternative, utilize task.factory.startnew passing in taskcreationoptions.longrunning task gets new thread (not affiliated threadpool) allow start , phone call cancel there. should solve threadpool exhaustion problem instead.

c# performance .net-4.5 c#-5.0 cancellationtokensource

Comments

Popular posts from this blog

web services - java.lang.NoClassDefFoundError: Could not initialize class net.sf.cglib.proxy.Enhancer -

Accessing MATLAB's unicode strings from C -

javascript - mongodb won't find my schema method in nested container -