views:

141

answers:

1

Hi eveybody, if i have a yield return in a lock statement does the lock get taken out on each yield (5 times in the example below) or only once for all the items in the list?

Thanks

    private List<string> _data = new List<string>(){"1","2","3","4","5"};
    private object _locker =new object();
    public IEnumerable<string> GetData()
    {
        lock (_locker)
        {
            foreach (string s in _data)
            {
                yield return s;
            }
        }
    }
+8  A: 

In your scenario, the lock will only be taken once. So in short, only once. However, you are not dealing with any shared resources. When you start to deal with shared resources like in the console app below, some interesting things happen.

You will see from the results that the lock is temporarily released on each yield. Also, note that the lock on List 1 is not released until all of the items have been written to the console, demonstrating that the GetData() method is executed partially with each iteration of the loop and that the lock must be released temporarily with each yield statement.

    static void Main(string[] args)
    {
        object locker = new object();
        IEnumerable<string> myList1 = new DataGetter().GetData(locker, "List 1");
        IEnumerable<string> myList2 = new DataGetter().GetData(locker, "List 2");
        Console.WriteLine("start Getdata");
        foreach (var x in myList1)
        {
            Console.WriteLine("List 1 {0}", x);
            foreach(var y in myList2)
            {
                Console.WriteLine("List 2 {0}", y);
            }
        }
        Console.WriteLine("end GetData");
        Console.ReadLine();
    }

    public class DataGetter
    {
        private List<string> _data = new List<string>() { "1", "2", "3", "4", "5" };

        public IEnumerable<string> GetData(object lockObj, string listName)
        {
            Console.WriteLine("{0} Starts", listName);
            lock (lockObj)
            {
                Console.WriteLine("{0} Lock Taken", listName);
                foreach (string s in _data)
                {
                    yield return s;
                }
            }
            Console.WriteLine("{0} Lock Released", listName);
        }
    }
}

Results:

            start Getdata
            List 1 Starts
            List 1 Lock Taken
            List 1 1
            List 2 Starts
            List 2 Lock Taken
            List 2 1
            List 2 2
            List 2 3
            List 2 4
            List 2 5
            List 2 Lock Released
            List 1 2
            List 2 Starts
            List 2 Lock Taken
            List 2 1
            List 2 2
            List 2 3
            List 2 4
            List 2 5
            List 2 Lock Released
            List 1 3
            List 2 Starts
            List 2 Lock Taken
            List 2 1
            List 2 2
            List 2 3
            List 2 4
            List 2 5
            List 2 Lock Released
            List 1 4
            List 2 Starts
            List 2 Lock Taken
            List 2 1
            List 2 2
            List 2 3
            List 2 4
            List 2 5
            List 2 Lock Released
            List 1 5
            List 2 Starts
            List 2 Lock Taken
            List 2 1
            List 2 2
            List 2 3
            List 2 4
            List 2 5
            List 2 Lock Released
            List 1 Lock Released
            end GetData

However, he really cool thing here is the results. Note that the line, "start GetData" occurs after the call to DataGetter().GetData() but before everything that occurs within the GetData() method. This is called deferred execution and it demonstrates the beauty and usefulness of the yield return statement: Anywhere within your outer loop you can break out of the loop and there will be no more calls to the inner loop. This means that you don't have to iterate the entire inner loop if you don't have to and it also means that you will start to get results to your outer loop earlier.

Daniel Dyson
This doesn't answer the question. Obviously "Lock Taken" and "Lock released" are called only once, but the question is whether "yield" will temporarily release the lock.
Tomas
Thanks @Tomas - I have changed the example above to reflect your comment
Daniel Dyson