Why Does Creating This New Task Crash My Program?

  • (3 Pages)
  • +
  • 1
  • 2
  • 3

35 Replies - 2373 Views - Last Post: 13 July 2012 - 03:48 PM Rate Topic: -----

#31 AdamSpeight2008  Icon User is online

  • MrCupOfT
  • member icon


Reputation: 1956
  • View blog
  • Posts: 8,694
  • Joined: 29-May 08

Re: Why Does Creating This New Task Crash My Program?

Posted 08 July 2012 - 03:48 PM

View PostCodingSup3rnatur@l-360, on 07 July 2012 - 11:59 PM, said:

I don't see a problem with using yield return ... inside a using statement. The resource being 'used' will get disposed when all items have been yielded, or when Dispose() gets called on the IEnumerator<T> object facilitating the iteration.


I don't understand how or when (if it does) when the Iterator part gets disposed off, because in my example I don't yield all of the possible chars of the streams. I just take the first one, which suggests (to me) the Iterator state-machine is still alive somewhere.

 using (var e=TT().GetEnumerator())
 {
   e.MoveNext();
   Console.WriteLine(e.Current);
  }  // Does this Dispose.

// snipped

  static System.Collections.Generic.IEnumerable<Char> TT()
  {
    using (var sr = new System.IO.StreamReader("TextFile1.txt"))
    {
      while (!sr.EndOfStream)
      { 
        yield return (char) sr.Read();  // Execution of this is paused here
      } 
    } // Cause this disposal to happen. 
  } 


This post has been edited by AdamSpeight2008: 08 July 2012 - 03:48 PM

Was This Post Helpful? 0
  • +
  • -

#32 Skydiver  Icon User is online

  • Code herder
  • member icon

Reputation: 1929
  • View blog
  • Posts: 5,741
  • Joined: 05-May 12

Re: Why Does Creating This New Task Crash My Program?

Posted 08 July 2012 - 05:25 PM

Adam posted a very good question. Had to spin up some code to convince myself empirically even though conceptually, I assumed that the correct disposes were happening.

Yes it does Dispose of things appropriately.
    class DisposableWrapper<T> : IDisposable
        where T : class, IDisposable
    {
        public T Inner { get; private set; }
        public string Label { get; private set; }

        public DisposableWrapper(T disposable, string label)
        {
            Inner = disposable;
            Label = label;
            Console.WriteLine("Construct {0}", Label);
        }

        ~DisposableWrapper()
        {
            Console.WriteLine("Destroy {0}", Label);
            Dispose(false);
        }

        public void Dispose()
        {
            Console.WriteLine("Dispose {0}", Label);
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        protected void Dispose(bool disposing)
        {
            Console.WriteLine("protected Dispose({1}) {0}", Label, disposing);
            if (disposing)
                Inner.Dispose();
            Inner = null;
        }
    }

    class Program
    {

        static System.Collections.Generic.IEnumerable<Char> TT()
        {
            Console.WriteLine("Before using in TT()");
            using (var sr = new DisposableWrapper<StreamReader>(new StreamReader(@"..\..\Program.cs"), "StreamReader"))
            {
                while (!sr.Inner.EndOfStream)
                {
                    Console.WriteLine("Before yield");
                    yield return (char) sr.Inner.Read();
                    Console.WriteLine("After yield");
                }
            }
            Console.WriteLine("After using in TT()");
        } 

        static void Main(string[] args)
        {
            Console.WriteLine("Before using in Main()");
            using (var e = new DisposableWrapper<IEnumerator<char>>(TT().GetEnumerator(), "Enumerator"))
            {
                e.Inner.MoveNext();
                Console.WriteLine(e.Inner.Current);
            }  // Does this Dispose.
            Console.WriteLine("After using in Main()");
        }
    }



Produces the following output:
Before using in Main()
Construct Enumerator
Before using in TT()
Construct StreamReader
Before yield
u
Dispose Enumerator
protected Dispose(True) Enumerator
Dispose StreamReader
protected Dispose(True) StreamReader
After using in Main()



The new thing that I learned today was that IEnumerator<T> are IDisposable. Thinking back now, it makes perfect sense, but it's shiny new discovery for me.

This post has been edited by Skydiver: 08 July 2012 - 05:30 PM

Was This Post Helpful? 1
  • +
  • -

#33 CodingSup3rnatur@l-360  Icon User is online

  • D.I.C Addict
  • member icon

Reputation: 916
  • View blog
  • Posts: 921
  • Joined: 30-September 10

Re: Why Does Creating This New Task Crash My Program?

Posted 09 July 2012 - 03:48 AM

Yes, Dispose() is called on the IEnumerator<char> object because you are using it in the context of a using statement. The IEnumerator<char> isn't special, it is just another IDisposable object as far as the using statement is concerned, and therefore the using statement will call Dispose() on it when execution reaches the end of the block, regardless of whether all items have been yielded or not.


Nevertheless, there are actually two notes of caution when using yield return within using blocks to bear in mind though:

Don't do something like this:

        static void Main(string[] args) {
            foreach (String line in ReadLines("TextFile1.txt")) {
                Console.WriteLine(line);
            }
        }
       
        static IEnumerable<String> ReadLines(String fileName) {
            using (StreamReader reader = new StreamReader(fileName)) {
                return ReadLines(reader);
            }
        }

        static IEnumerable<String> ReadLines(TextReader reader) {
            String line = null;
            while ((line = reader.ReadLine()) != null) {
                yield return line;
            }
        }



If we called ReadLines(String), and then iterated over the returned IEnumerable<String>, we would find that the StreamReader object had been disposed of prematurely, as the using statement hadn't been factored in to the state machine as the yield return appeared in a different method to the using statement. In order to be correctly factored into the state machine, the using statement has to be in the same method as the yield return ... statement.


Secondly, if you have a method that takes an IDisposable object as an argument, and it is the method's job to dispose of the IDisposable object when it is finished with it, and the method uses yield, don't do this:

        IEnumerable<String> ReadLines(TextReader reader) {
            using (reader) {
                String line = null;
                while ((line = reader.ReadLine()) != null) {
                    yield return line;
                }
            }
        }



do this:

        IEnumerable<String> ReadLines(Func<TextReader> readerProvider) {
            using (TextReader reader = readerProvider()) {
                String line = null;
                while ((line = reader.ReadLine()) != null) {
                    yield return line;
                }
            }
       }



and then call it like this:

ReadLines(() => new StreamReader("TextFile1.txt"));

That serves two purposes:

1) It means that if something happens before the call to MoveNext() (or MoveNext() never gets called), there are no problems with the TextReader not being disposed of. The deferred execution model could actually cause issues with the TextReader object not been disposed of if the first snippet was used.

2) It means that every time GetEnumerator() is called on the returned IEnumerable<String> object, a brand new TextReader object is used in the state machine, rather than reusing any existing ones from previous calls to GetEnumerator().
Was This Post Helpful? 3
  • +
  • -

#34 AdamSpeight2008  Icon User is online

  • MrCupOfT
  • member icon


Reputation: 1956
  • View blog
  • Posts: 8,694
  • Joined: 29-May 08

Re: Why Does Creating This New Task Crash My Program?

Posted 09 July 2012 - 05:01 AM

Great to see some of our members putting the Yeoman like work, to test a hypothesis and experiment to check the actual results.
Skydiver CodingSup3rnatur@l-360 superb.

This post has been edited by AdamSpeight2008: 09 July 2012 - 05:16 AM

Was This Post Helpful? 1
  • +
  • -

#35 adn258  Icon User is offline

  • D.I.C Addict

Reputation: 4
  • View blog
  • Posts: 536
  • Joined: 31-August 11

Re: Why Does Creating This New Task Crash My Program?

Posted 13 July 2012 - 03:41 PM

View PostSkydiver, on 06 July 2012 - 04:25 PM, said:

And as final proof... MSDN documents the using statement as translation into a try-finally{} block where the finally block includes the Dispose() call:
http://msdn.microsof...y/yh598w02.aspx

So to show that a return call with in a try-finally block still calls the finally:
        static void DoTryFinally()
        {
            Console.WriteLine("DoTryFinally enter");
            try
            {
                Console.WriteLine("Try");
                return;
            }
            finally
            {
                Console.WriteLine("Finally");
            }
            Console.WriteLine("DoTryFinally leave");
        }


which produces the following output:
DoTryFinally enter
Try
Finally


Showing that the finally block is called even with return statement as the second line within the try block.


Since we have been talking about this whole using( issue I am wondering and just want to make sure

you take code like the following

try
            {
                using (WebClient wc = new WebClient())
                {
                    return (wc.DownloadString(strWebLocation));
                }
            }
            catch
            {
              //do something here for exception
            }



if you are using an object like in this case a webclient is it disposed even though the using code itself is within a try and catch? I don't see how it still couldn't be disposed but I'm just making sure if this is dangerous at all.
Was This Post Helpful? 0
  • +
  • -

#36 CodingSup3rnatur@l-360  Icon User is online

  • D.I.C Addict
  • member icon

Reputation: 916
  • View blog
  • Posts: 921
  • Joined: 30-September 10

Re: Why Does Creating This New Task Crash My Program?

Posted 13 July 2012 - 03:48 PM

Yes, that's fine, and the WebClient will be disposed no problem.

It is equivalent to this:

try {
     WebClient wc = new WebClient();
     try {
          return (wc.DownloadString(strWebLocation));
     } finally {
         if (wc != null) {
             ((IDisposable)wc).Dispose();
         }
     }
} catch {
     //do something here for exception
}



which poses no danger.

This post has been edited by CodingSup3rnatur@l-360: 14 July 2012 - 04:31 AM

Was This Post Helpful? 1
  • +
  • -

  • (3 Pages)
  • +
  • 1
  • 2
  • 3