Страницы

Поиск по вопросам

суббота, 21 декабря 2019 г.

Yield и потоки и все все все

#c_sharp #многопоточность #yield


Оператор yield

class UserCollection
{
    public static IEnumerable Power()
    {
        yield return "Hello world!";
    }
}


Оператор yield из .Net Reflector - перенесенный в рялии C# и Visual Studio

class UserCollection
{
    public static IEnumerable Power()
    {
        return new ClassPower(-2);
    }

    private sealed class ClassPower : IEnumerable,  IEnumerator,
IEnumerator, IDisposable
    {
        // Поля.
        private int state;
        private object current;
        private int initialThreadId;

        // Конструктор.
        public ClassPower(int state)
        {
            this.state = state;
            this.initialThreadId = Thread.CurrentThread.ManagedThreadId;
        }

        //private bool IEnumerator.MoveNext() // Так в Рефлекторе
        bool IEnumerator.MoveNext()
        {
            switch (this.state)
            {
                case 0:
                    this.state = -1;
                    this.current = "Hello world!";
                    this.state = 1;
                    return true;

                case 1:
                    this.state = -1;
                    break;
            }
            return false;
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            if ((Thread.CurrentThread.ManagedThreadId == this.initialThreadId) &&
(this.state == -2))
            {
               this.state = 0;
               return this;
            }
            return new UserCollection.ClassPower(0);
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            // Так в Рефлекторе 
            //return this.System.Collections.Generic.IEnumerable.GetEnumerator(); 

            return (this as IEnumerable).GetEnumerator();
        }

        void IEnumerator.Reset()
        {
            throw new NotSupportedException();
        }

        void IDisposable.Dispose()
        {
        }

        // Свойства.
        object IEnumerator.Current
        {
            get
            {
                return this.current;
            }
        }

        object IEnumerator.Current
        {
            get
            {
                return this.current;
            }
        }
    }
}


Меня интересует строка и как ее комментирует автор

this.initialThreadId = Thread.CurrentThread.ManagedThreadId;


данная строка свидетельствует о синхронизации и доступа к этой коллекции как к разделяемому
ресурсу.
Почему именно при работе с yield коллекция воспринимается как разделяемый ресурс
и нужна вообще в принципе работа с потоками?

См. рисунок как я понял работу оператора yield
и может т.к это в эту коробку все операторы yield помещают свое значение , то может
поэтому нужна работа с потоками? 

    


Ответы

Ответ 1



Обратите внимание на функцию IEnumerable.GetEnumerator(). Данный объект реализует как IEnumerable, так и IEnumerator. Состояние -2 означает, что объект «свежий», только что вернулся из вызова функции Power, и его энумерация ещё не начиналась. В этой ситуации, когда тут же вызывается GetEnumerator в том же потоке (а это происходит в подавляющем большинстве случаев: например, при вызове foreach (var x in Power())), то из соображений эффективности можно вернуть этот же объект (т. к. он служит и энумератором тоже). Но если энумерация уже прошла, внутреннее состояние объекта может быть испорчено, для этого случая возвращают новый объект. Точно так же если энумерация производится в другом потоке, то чтобы избежать необходимости синхронизации, лучше создать новый объект. Остановимся специально на последнем пункте. Если вдруг IEnumerable уйдёт в другой поток, и оттуда буден вызван метод GetEnumerator(), и одновременно в основном потоке будет тоже вызван этот же метод, то без проверки thread id может случиться так, что оба потока пройдут одновременно проверку this.state == -2, и получат один и тот же объект! В результате при энумерации они будут мешать друг другу. В этом случае мы даём «выиграть» тому же потоку, который создал объект. Таким образом, это специальная оптимизация для обычного случая (выделение только одного объекта); «необычные» случаи использования проходят более сложным путём. Подробнее по теме: Jon Skeet, C# in Depth. Iterator block implementation details: auto-generated state machines.

Комментариев нет:

Отправить комментарий