From 7cda0fc470eb09090b7a7369ea276c8adc62f93b Mon Sep 17 00:00:00 2001 From: "gu.martinm@gmail.com" Date: Wed, 28 May 2014 03:51:11 +0200 Subject: [PATCH] No time for comments --- .../HttpClientsExamples/WebClientExample.cs | 205 +++++++++++---------- Allgemeines/Threads/Threads/Chapter3.cs | 5 + 2 files changed, 117 insertions(+), 93 deletions(-) diff --git a/Allgemeines/HttpClientsExamples/HttpClientsExamples/WebClientExample.cs b/Allgemeines/HttpClientsExamples/HttpClientsExamples/WebClientExample.cs index f2f80e2..641fa48 100644 --- a/Allgemeines/HttpClientsExamples/HttpClientsExamples/WebClientExample.cs +++ b/Allgemeines/HttpClientsExamples/HttpClientsExamples/WebClientExample.cs @@ -25,18 +25,13 @@ namespace HttpClientsExamples // requested URI contains a query. client.Headers.Add("user-agent", "Mozilla/4.0 (compatible; Linux; Mono .NET 4.5)"); - using (Stream data = client.OpenRead (line)) - using (StreamReader reader = new StreamReader(data)) + using (Stream replyStream = client.OpenRead (line)) + using (StreamReader replyStreamReader = new StreamReader(replyStream)) { - string s = reader.ReadToEnd(); + string s = replyStreamReader.ReadToEnd(); Console.WriteLine(s); } - // data.Dispose(); - // data.Close(); - // reader.Dispose(); - // reader.Close(); } - // client.Dispose(); line = null; @@ -47,54 +42,73 @@ namespace HttpClientsExamples Console.WriteLine("Write URI: "); line = Console.ReadLine(); } - using (WebClient client = new WebClient()) - { - // Another way, using DownloadDataAsync and its delegate. - // WebClient.DownloadStringCompleted - // client.DownloadDataCompleted += new DownloadDataCompletedEventHandler(DownloadDataCallback); - // client.DownloadDataAsync (uri, waiter); - // We may retrieve the data using WebClient.DownloadDataCompletedEventArgs.Result in the - // delegate implementation (our callback/lambda expression/delegate implementation) - client.OpenReadCompleted += new OpenReadCompletedEventHandler(OpenReadCallback); - - // Taken from: http://msdn.microsoft.com/en-us/library/wewwczdw.aspx - // Asynchronous Method Overloads: - // There are potentially two overloads for the asynchronous operations: single-invocation and - // multiple-invocation. You can distinguish these two forms by their method signatures: the - // multiple-invocation form has an extra parameter called userState. This form makes it possible - // for your code to call Method1Async(string param, object userState) multiple times without - // waiting for any pending asynchronous operations to finish. If, on the other hand, you try to - // call Method1Async(string param) before a previous invocation has completed, the method - // raises an InvalidOperationException. - - // The userState parameter for the multiple-invocation overloads allows you to distinguish - // among asynchronous operations. You provide a unique value (for example, a GUID or hash code) - // for each call to Method1Async(string param, object userState), and when each operation - // is completed, your event handler can determine which instance of the operation raised - // the completion event. - - // Tracking Pending Operations: - // If you use the multiple-invocation overloads, your code will need to keep track of - // the userState objects (task IDs) for pending tasks. For each call to - // Method1Async(string param, object userState), you will typically generate a new, - // unique userState object and add it to a collection. When the task corresponding to this - // userState object raises the completion event, your completion method implementation will - // examine AsyncCompletedEventArgs.UserState and remove it from your collection. Used this way, - // the userState parameter takes the role of a task ID. - - // You must be careful to provide a unique value for userState in your calls to multiple-invocation - // overloads. Non-unique task IDs will cause the asynchronous class throw an ArgumentException. - var taskId = Guid.NewGuid(); - client.OpenReadAsync(new Uri(line), taskId); - client.Dispose (); - // I could make more calls in this way: - // taskId = Guid.NewGuid(); - // client.OpenReadAsync(new Uri(line), taskId); - // taskId = Guid.NewGuid(); - // client.OpenReadAsync(new Uri(line), taskId); - } - // How to wait for client end without using sleep? I am afraid, there is no way :( - Console.WriteLine ("Press any key to continue after receiving data."); + /** + * IT IS IMPOSSIBLE TO CALL WebClient.Dispose() without creating a complete mess. :/ + * Besides, calling Dispose() does not seem really useful (AFAIU after reading the Mono implementation) + * and this answer from SE: http://codereview.stackexchange.com/questions/9714/using-statement-in-context-of-streams-and-webclients + * seems to match what I have already seen in the Mono code. So, I will use the using statement + * when working with WebClient class in a synchronous way even when I know it probably does not do anything useful + * and will not bother calling WebClient.Dispose() when using WebClient in asynchronous way because + * the code ends up being too complex. + * Hopefully this will work smoothly :) + */ + WebClient webClientAsync = new WebClient(); + + // Another way, using DownloadDataAsync and its delegate. + // WebClient.DownloadStringCompleted + // client.DownloadDataCompleted += new DownloadDataCompletedEventHandler(DownloadDataCallback); + // client.DownloadDataAsync (uri, waiter); + // We may retrieve the data using WebClient.DownloadDataCompletedEventArgs.Result in the + // delegate implementation (our callback/lambda expression/delegate implementation) + webClientAsync.OpenReadCompleted += new OpenReadCompletedEventHandler(OpenReadCallback); + + // Taken from: http://msdn.microsoft.com/en-us/library/wewwczdw.aspx + // Asynchronous Method Overloads: + // There are potentially two overloads for the asynchronous operations: single-invocation and + // multiple-invocation. You can distinguish these two forms by their method signatures: the + // multiple-invocation form has an extra parameter called userState. This form makes it possible + // for your code to call Method1Async(string param, object userState) multiple times without + // waiting for any pending asynchronous operations to finish. If, on the other hand, you try to + // call Method1Async(string param) before a previous invocation has completed, the method + // raises an InvalidOperationException. + + // The userState parameter for the multiple-invocation overloads allows you to distinguish + // among asynchronous operations. You provide a unique value (for example, a GUID or hash code) + // for each call to Method1Async(string param, object userState), and when each operation + // is completed, your event handler can determine which instance of the operation raised + // the completion event. + + // Tracking Pending Operations: + // If you use the multiple-invocation overloads, your code will need to keep track of + // the userState objects (task IDs) for pending tasks. For each call to + // Method1Async(string param, object userState), you will typically generate a new, + // unique userState object and add it to a collection. When the task corresponding to this + // userState object raises the completion event, your completion method implementation will + // examine AsyncCompletedEventArgs.UserState and remove it from your collection. Used this way, + // the userState parameter takes the role of a task ID. + + // You must be careful to provide a unique value for userState in your calls to multiple-invocation + // overloads. Non-unique task IDs will cause the asynchronous class throw an ArgumentException. + var taskId = Guid.NewGuid(); + webClientAsync.OpenReadAsync(new Uri(line), taskId); + /** + * I WILL NOT CALL Dispose when using WebClient in asynchronous way. + * webClientAsync.Dispose (); + */ + + // I could make more calls in this way BUT JUST ONCE THE LAST ASYNC OPERATION WAS COMPLETED!!!! + // From here: http://stackoverflow.com/questions/9765109/webclient-does-not-support-concurrent-i-o-operations + // And here: http://stackoverflow.com/questions/7905445/wb-downloadfileasync-throw-webclient-does-not-support-concurrent-i-o-operations + // When calling DownloadFileAsync method you have to make sure it completes before trying to download again. + // + // So, I should wait until the async operation is completed and then I could use OpenReadAsync again, but not before. :( + // taskId = Guid.NewGuid(); + // webClientAsync.OpenReadAsync(new Uri(line), taskId); + // taskId = Guid.NewGuid(); + // webClientAsync.OpenReadAsync(new Uri(line), taskId); + + // How to wait for client end without using sleep? I am afraid, there is no nice way. Anyhow if you are using Async, + // why would you want to wait? Console.Read(); @@ -106,47 +120,42 @@ namespace HttpClientsExamples Console.WriteLine("Write URI: "); line = Console.ReadLine(); } - using (WebClient client = new WebClient()) - using (Task data = client.OpenReadTaskAsync(line)) - //using (Stream stream = data.Result) + using (WebClient client = new WebClient ()) { - data.Start(); + // AFAIK (see comments in some place above) I do not need to call WebClient.Dispose. Anyhow, in this case, the code + // is not too complicated so, I will write the using statement. + + /** + * Should I call Task.Dispose()? Answer: + * DO NOT BOTHER DISPOSING OF YOUR TASKS: http://blogs.msdn.com/b/pfxteam/archive/2012/03/25/10287435.aspx + */ + Task task = client.OpenReadTaskAsync (line); + task.Start (); try { - Task.WaitAll(data); - } - catch (AggregateException ae) - { - ae.Handle(e => - { - if (e is OperationCanceledException) - { - Console.WriteLine("Cancelling a Task, catching OperationCanceledException: {0} {1}", e.Message, e.StackTrace); - return true; - } - else - { - Console.WriteLine("Cancelling a Task, dunno what are you: {0} {1}", e.Message, e.StackTrace); - return false; - } - }); + Task.WaitAll (task); + } catch (AggregateException ae) { + ae.Handle (e => { + if (e is OperationCanceledException) { + Console.WriteLine ("Cancelling a Task, catching OperationCanceledException: {0}", e); + return true; + } else { + Console.WriteLine ("Cancelling a Task, dunno what are you: {0}", e); + return false; + } + }); } - Stream dataStream = data.Result; - if (dataStream != null) + // I am starting to love the using statement instead of traditional try/finally block with check for null values and close. + using (Stream replyStream = task.Result) + using (StreamReader replyStreamReader = new StreamReader (replyStream)) { - + string s = replyStreamReader.ReadToEnd (); + Console.WriteLine (s); } } } private void OpenReadCallback (Object sender, OpenReadCompletedEventArgs eventData) { - Stream replyStream = null; - StreamReader replyStreamReader = null; - - // Old fashined way (without using) - // NOTE: using statement is the same as this code (it also checks for null value before calling - // the Dispose method) - var taskId = (Guid) eventData.UserState; if (eventData.Cancelled) @@ -158,20 +167,30 @@ namespace HttpClientsExamples Exception errorException = eventData.Error; if (errorException != null) { - Console.WriteLine ("Task with Exception. Taks Id: {0}, Exception: ", taskId, errorException); + Console.WriteLine ("Task with Exception. Taks Id: {0}, Exception: {1}", taskId, errorException); return; } + + // Old fashined way (without using) + // NOTE: using statement is the same as this code (it also checks for null value before calling + // the Dispose method) + Stream replyStream = null; + StreamReader replyStreamReader = null; - + Console.WriteLine ("Taks Id: {0}", taskId); try { - // throw new Exception("It will become an UnHandled Exception if the main thread does not cath it" + - // "and it will kill my application :("); + // WE MUST ALWAYS CLOSE THE STREAM RETURNED BY WebClient!!!! replyStream = (Stream) eventData.Result; replyStreamReader = new StreamReader (replyStream); Console.WriteLine (replyStreamReader.ReadToEnd ()); - // throw new Exception("If I throw exception here, weird things will happen. Shouldn't it become" + - // "a UnHandled Exception?"); + // throw new Exception("My Exception from Async CallBack"); + // IF YOU SEE THE MONO IMPLEMENTATION OF OpenReadAsync YOU WILL SEE THERE IS A try/catch Exception + // AND THAT CATCH EXCEPTION IS CALLING AGAIN MY OpenReadCallback. When calling OpenReadCallback in the catch + // Exception block eventData.Error will not be null. So, my OpenReadCallback could be called more than once if + // there are multiple Exceptions being thrown from my OpenReadCallback method. :/ + // BECAUSE OF THAT YOU MUST ALWAYS CHECK IN THE CALLBACK IMPLEMENTATION THE eventData.Cancelled AND eventData.Error VALUES!!!! + // (As I've done here) } finally { diff --git a/Allgemeines/Threads/Threads/Chapter3.cs b/Allgemeines/Threads/Threads/Chapter3.cs index cb2311b..949e672 100644 --- a/Allgemeines/Threads/Threads/Chapter3.cs +++ b/Allgemeines/Threads/Threads/Chapter3.cs @@ -34,6 +34,11 @@ namespace Threads Task t2 = Task.Factory.StartNew(DoRight); Task.WaitAll(t1, t2); + /** + * Should I call Task.Dispose()? Answer: + * DO NOT BOTHER DISPOSING OF YOUR TASKS: http://blogs.msdn.com/b/pfxteam/archive/2012/03/25/10287435.aspx + */ + int n = 20; Console.WriteLine("Handling Exceptions 1/2: The Handle Method"); -- 2.1.4