Каково практическое применение использования неизменяемого типа потокобезопасным способом, который отличается от использования изменяемого типа таким же образом? - PullRequest
0 голосов
/ 14 февраля 2019

Рассмотрим следующий код:

    class Program
    {
        static object locker = new object();
        static string data;

        static void Main(string[] args)
        {
            Task.Factory.StartNew(async () =>
            {
                while(true)
                {
                    await Task.Delay(5000);

                    string localCopy;
                    lock (locker)
                    {
                        localCopy = data;
                    }

                    // do some read operation with localCopy; 
                    // write to log file, call a web API, etc
                    Log(localCopy);
                }
            });

            while(true)
            {
                // data is written to from time to time on the main thread;
                // can be user input, etc.
                string input = Console.ReadLine();

                lock(locker)
                {
                    data = input;
                }
            }
        }
    }

Поскольку .NET, строки являются неизменяемыми, и одним из преимуществ неизменяемости является безопасность потоков, нужны ли операторы lock?

EDIT : я выбрал неизменный тип, string в приведенном выше примере, только для контекста;Обычно я пытаюсь понять свойство «поточно-ориентированного» неизменяемого типа, если, основываясь на комментариях (и моем собственном понимании вещей), в многопоточном коде при использовании таких типов все еще необходима какая-то семантика lock.кросс-нить.

1 Ответ

0 голосов
/ 14 февраля 2019

Каково практическое применение использования неизменяемого типа потокобезопасным способом, который отличается от использования изменяемого типа таким же образом?

Как отмечено в комментариях, этовсе о переменных.

Если у вас есть несколько потоков, обращающихся к одной и той же переменной, то да, вам нужно каким-то образом защитить переменную (lock, Interlocked и т. д.).

Преимущество неизменяемых типов проявляется, когда вы передаете эти данные другому потоку - создавая другую переменную.Все, что вам нужно сделать, это скопировать ссылку из одной переменной в другую, и теперь первая переменная может измениться так, как она того пожелает;вторая переменная остается неизменной.

Я думаю, что это немного легче понять на примере, подобном ImmutableStack<string>.Допустим, есть «главный» поток, который выдвигает и выталкивает ImmutableStack<string>;так как это является неизменным, каждый push / pop обновляет свою собственную переменную.Если наш «основной» поток хочет дать другому потоку снимок , он просто копирует свою текущую переменную в другую переменную для этого потока.Затем «основной» поток может безнаказанно продолжать нажимать / выталкивать / обновлять свою собственную переменную.«Вторичный» поток имеет свой собственный неизменяемый снимок.

В более общей ситуации это может быть полезно для одного или нескольких считывателей / респондентов, где каждый цикл «чтения» начинается с захвата текущего состояния общего ресурса.переменной и используя эту локальную копию для продолжительности цикла.

Если вы хотите сделать моментальный снимок изменяемого значения, для этого потребуется выполнить глубокое клонирование.Представьте, что string был изменчив, как это происходит на других языках.В этом случае копирование значения (ссылки) строки будет недостаточным;один поток может изменить один символ, в то время как другой поток пытается сделать что-то еще со значением.Чтобы получить настоящий моментальный снимок изменяемого строкового значения, вам необходимо скопировать всю строку в новую строку.

Существуют и другие преимущества для неизменяемых типов в целом (дизайн и т. Д.), Но этоПреимущество «ссылочного снимка» - это то, что особенно выгодно для многопоточности.

...