если у меня есть возврат урожая в операторе блокировки, блокировка становится вынутой на каждом урожае (5 раз в примере ниже) или только однажды для всех объектов в списке?
Спасибо
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;
}
}
}
Редактировать: Этот ответ был неправильным, но я не могу удалить его, так как он был отмечен как правильный. Пожалуйста, смотрите ответ @Lockszmith ниже для правильного ответа.
Перефразировано:
Блокировка НИКОГДА не снимается между каждым возвратом дрозда. ПРИМЕЧАНИЕ: Однако она освобождается, когда перечислитель завершает работу, т.е. когда цикл foreach заканчивается.
Конец редактирования
Оригинальный ответ (неверный):
В вашем сценарии блокировка будет снята только один раз. Короче говоря, только один раз. Однако вы не имеете дело с общими ресурсами. Когда вы начинаете работать с общими ресурсами, как в приведенном ниже консольном приложении, происходят интересные вещи.
Из результатов видно, что блокировка временно снимается на каждом выходе. Также обратите внимание, что блокировка списка 1 не снимается до тех пор, пока все элементы не будут записаны в консоль, что свидетельствует о том, что метод GetData() выполняется частично на каждой итерации цикла и что блокировка должна временно сниматься при каждом операторе yield.
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);
}
}
}
Результаты:
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
Однако по-настоящему интересным здесь являются результаты. Обратите внимание, что строка "start GetData" появляется после вызова DataGetter().GetData(), но до всего, что происходит внутри метода GetData(). Это называется отложенным выполнением и демонстрирует красоту и полезность оператора возврата yield: В любом месте вашего внешнего цикла вы можете выйти из цикла, и вызовов внутреннего цикла больше не будет. Это означает, что вам не придется выполнять итерации всего внутреннего цикла, если в этом нет необходимости, а также то, что вы начнете получать результаты во внешнем цикле раньше.