yield语句中的yield返回

如果我在一个锁定语句中有一个收益率回报,锁定会在每个收益率上取消(在下面的例子中为5次),或者只对列表中的所有项目取消一次?

谢谢

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

编辑: 这个答案是错误的,但我不能删除它,因为它被标记为正确。 请参阅下面的@ Lockszmith的答案以获得正确的答案。

转述:

锁定永远不会在每个回归之间释放。 注意:但是在枚举器完成时,即foreach循环结束时,它会被释放。

结束编辑

原答案(错误):

在您的方案中,锁定将只进行一次。 所以简而言之,只有一次。 但是,您不处理任何共享资源。 当您开始处理下面的控制台应用程序中的共享资源时,会发生一些有趣的事情。

您将从结果中看到锁定在每个产量上暂时释放。 另请注意,在将所有项目都写入控制台之前,列表1上的锁定不会被释放,这表明GetData()方法是在循环的每次迭代中部分执行的,并且必须临时释放锁定收益率表。

  static void Main(string[] args) { object locker = new object(); IEnumerable myList1 = new DataGetter().GetData(locker, "List 1"); IEnumerable 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 _data = new List() { "1", "2", "3", "4", "5" }; public IEnumerable 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); } } } 

结果:

  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 

但是,他真的很酷的事情就是结果。 请注意,在调用DataGetter()。GetData()之后,但在GetData()方法中发生的所有事情之前,都会出现“start GetData”这一行。 这称为延迟执行,它演示了yield return语句的优点和用处:在外部循环中的任何地方都可以突破循环,并且不再需要调用内部循环。 这意味着如果不需要,则不必迭代整个内循环,这也意味着您将开始先将结果传递到外循环。

开始编辑
请参阅@EZI提供的社区维基中的代码,它更易于阅读/清除。
结束编辑

很抱歉从死里复活,但是阅读丹尼尔接受的答案,然后自己测试我虽然至少那10个投票的人至少应该知道这是完全错误的。

答案是: 每次yeald return之间都不会释放锁定
注意 :但是在枚举器完成时,即foreach循环结束时,它会被释放。

Daniel的回答是错误的,声称锁定不止一次。 那是因为Daniel的代码不是multithreading的,它总是以相同的方式计算。 该代码中的锁定只采用一次,因为它是相同的线程,所以它始终是相同的锁。

我从他的答案中获取了@Daniel的代码,并将其更改为使用2个线程,一个用于List1,另一个线程为List2的每次迭代创建。

正如您所看到的,一旦t2线程启动,线程将死锁,因为t2正在等待永远不会被释放的锁。

代码:

 void Main() { object locker = new object(); IEnumerable myList0 = new DataGetter().GetData(locker, "List 0"); IEnumerable myList1 = new DataGetter().GetData(locker, "List 1"); IEnumerable myList2 = new DataGetter().GetData(locker, "List 2"); Console.WriteLine("start Getdata"); // Demonstrate that breaking out of a foreach loop releasees the lock var t0 = new Thread(() => { foreach( var s0 in myList0 ) { Console.WriteLine("List 0 {0}", s0); if( s0 == "2" ) break; } }); Console.WriteLine("start t0"); t0.Start(); t0.Join(); // Acts as 'wait for the thread to complete' Console.WriteLine("end t0"); // t1's foreach loop will start (meaning previous t0's lock was cleared var t1 = new Thread(() => { foreach( var s1 in myList1) { Console.WriteLine("List 1 {0}", s1); // Once another thread will wait on the lock while t1's foreach // loop is still active a dead-lock will occure. var t2 = new Thread(() => { foreach( var s2 in myList2 ) { Console.WriteLine("List 2 {0}", s2); } } ); Console.WriteLine("start t2"); t2.Start(); t2.Join(); Console.WriteLine("end t2"); } }); Console.WriteLine("start t1"); t1.Start(); t1.Join(); Console.WriteLine("end t1"); Console.WriteLine("end GetData"); } void foreachAction( IEnumerable target, Action action ) { foreach( var t in target ) { action(t); } } public class DataGetter { private List _data = new List() { "1", "2", "3", "4", "5" }; public IEnumerable 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); } } 

@Lockszmith有很好的收获(+1)。 因为我发现他的代码难以阅读,所以我只发布这个。 这是一个“社区维基” 。 随意更新。

 object lockObj = new object(); Task.Factory.StartNew((_) => { System.Diagnostics.Debug.WriteLine("Task1 started"); var l1 = GetData(lockObj, new[] { 1, 2, 3, 4, 5, 6, 7, 8 }).ToList(); }, TaskContinuationOptions.LongRunning); Task.Factory.StartNew((_) => { System.Diagnostics.Debug.WriteLine("Task2 started"); var l2 = GetData(lockObj, new[] { 10, 20, 30, 40, 50, 60, 70, 80 }).ToList(); }, TaskContinuationOptions.LongRunning); 

 public IEnumerable GetData(object lockObj, IEnumerable list) { lock (lockObj) { foreach (T x in list) { System.Diagnostics.Debug.WriteLine("Thread " + Thread.CurrentThread.ManagedThreadId + " returned " + x ); Thread.Sleep(1000); yield return x; } } }