Типы действительно ли значения неизменны по определению?

Мне кажется, что у вас нет проблем с самим curl, но вы делаете слишком много соединений одновременно с прокси-серверами, если в соединениях отказано. Вы можете быть в черном списке навсегда или на какой-то период.

Проверьте это, запустив свой curl с текущего IP и сделайте stat: сколько соединений было установлено, сколько отказано, сколько истекло. Сделайте это несколько раз и наберите в среднем. Затем измените сервер на другой, который имеет другой IP-адрес, и проверьте, какая у вас там статистика. При первом запуске у вас должна быть намного лучшая статистика, которая, вероятно, если вы повторите тест на новом IP, станет только хуже. Хорошей идеей может быть не использовать весь пул прокси-серверов для подключения к статистике, а выбрать из них срез, проверить фактический IP-адрес и повторить эту проверку нового IP-адреса, поэтому, если причина в том, что вы злоупотребляете сервисом, вы не попадете в черный список все прокси, но все еще будут иметь следующую группу «нетронутых» прокси, чтобы проверить их на новом IP, если это действительно так. Помните, что даже если IP-адреса прокси находятся в разных местах, они могут принадлежать одному и тому же поставщику услуг. Вероятно, у него есть один список злоупотреблений для всех их прокси-серверов, поэтому, если вас плохо видят количество запросов, которые вы делаете в одной стране, вас могут заблокировать и в другой стране, даже до того, как вы подключитесь к прокси-серверу другой страны. [ 1126]

Если вы все еще хотите проверить, не скручивается ли это, вы можете настроить тестовую среду с несколькими подачами. Эту среду тестирования вы можете передать сопровождающему curl, чтобы он мог воспроизвести ошибку. Вы можете использовать docker и создавать 10, 20 или 100 прокси-серверов и подключаться к ним, чтобы увидеть, есть ли у curl проблема или нет.

вам понадобится докер , его можно установить на Win / Mac / Linux
один из прокси-образов для создания прокси
create сетевое учебное пособие для контейнеров (мост должен быть в порядке)
подключать контейнеры к сети - сеть
хорошо установить для каждого прокси-контейнера свои - ip
делают возможным для каждого прокси-контейнера читать конфигурацию и записывать журнал ошибок (так что вы можете узнать, почему они отключились, если это произойдет) с помощью mountig log / config files / direcotires с помощью - том
и все прокси-контейнеры должны быть запущены

вы можете подключиться к прокси, который работает внутри контейнера двумя способами. если вы хотите, чтобы curl находился за пределами этих контейнеров, вам нужно открыть с помощью -p порты этих прокси от контейнера до внешнего мира (curl в вашем случае).

или

вы можете использовать другое изображение контейнера с linux + curl. Например, Alpine linux + curl и подключите его к той же сети, что и с прокси. Если вы делаете это, вам не нужно публиковать (выставлять) порты прокси и не нужно думать о том, какой номер порта прокси я должен предоставить для этого конкретного прокси.

на каждом шаге вы можете выполнить команду

docker ps -a

, чтобы увидеть все контейнеры и их состояние.

чтобы остановить и удалить все контейнеры (не изображения, из которых они исходят, а запущенные контейнеры) на случай, если у вас возникли ошибки с контейнером, который вышел.

docker stop $(docker ps -aq) && docker rm $(docker ps -aq)

или для остановки и удаления из списка определенного контейнера

docker stop 
docker rm 

, чтобы увидеть все контейнеры, которые подключены к мостовой сети (по умолчанию)

docker network inspect bridge

Если вы подтвердите, что действительно существует проблема с подключением к прокси, которые находятся на вашем локальном компьютере, тогда это то, что сопровождающий curl может воспроизвести.

просто поместите все команды, как указано выше, чтобы создать все прокси-серверы, подключить их к сети и т. Д. В файле, например сценарий replicate.sh, начиная с

#!/bin/sh

and your comands here

, сохранить этот файл и выполнить команду

[115 ]

, чтобы сделать его исполняемым.

Вы можете запустить его, чтобы перепроверить, все ли работает должным образом

./replicate.sh

и отправить сопровождающего curl для репликации среды, в которой у вас возникла проблема.

Если вы не хотите запускать прокси для запуска множества команд, таких как doker run, вы можете использовать вместо этого docker compose , который позволяет вам определять всю среду тестирования в одном файле. [1143 ]

Если вы запускаете много контейнеров, вы можете ограничить ресурсы, например память , которую использует каждый из них, может помочь вам в случае такого количества прокси

40
задан Jon Seigel 27 February 2010 в 03:51
поделиться

11 ответов

Объект неизменен, если его состояние не меняется после того, как объект был создан.

Краткий ответ: Нет, типы значений не неизменяемы по определению. И структуры, и классы могут быть изменяемыми или неизменяемыми. Возможны все четыре комбинации. Если у структуры или класса есть общедоступные поля, не предназначенные только для чтения, общедоступные свойства с установщиками или методы, которые устанавливают частные поля, они являются изменяемыми, поскольку вы можете изменить его состояние, не создавая новый экземпляр этого типа.


Длинный ответ: первый из все, вопрос неизменяемости применяется только к структурам или классам с полями или свойствами. Самые основные типы (числа, строки и null) по своей сути неизменяемы, потому что в них нечего изменить (поле / свойство). 5 - это 5 - это 5. Любая операция над 5 возвращает только другое неизменяемое значение.

Вы можете создавать изменяемые структуры, такие как System.Drawing.Point . Оба X и Y имеют сеттеры, которые изменяют поля структуры:

Point p = new Point(0, 0);
p.X = 5;
// we modify the struct through property setter X
// still the same Point instance, but its state has changed
// it's property X is now 5

Некоторые люди, кажется, путают неизменность с тем фактом, что типы значений передаются по значению (отсюда и их название), а не по ссылке.

void Main()
{
    Point p1 = new Point(0, 0);
    SetX(p1, 5);
    Console.WriteLine(p1.ToString());
}

void SetX(Point p2, int value)
{
    p2.X = value;
}

В этом случае Console.WriteLine () пишет « {X = 0, Y = 0} ». Здесь p1 не был изменен, потому что SetX () модифицировал p2 , который является копией p1 . Это происходит потому, что p1 является типом значения , а не потому, что он неизменяемый (это не так).

Почему должен типы значений быть неизменными? Множество причин ... См. этот вопрос . В основном это потому, что изменяемые типы значений приводят к разного рода не столь очевидным ошибкам. В приведенном выше примере программист мог ожидать, что p1 будет (5, 0) после вызова SetX () . Или представьте себе сортировку по значению, которое впоследствии может измениться. Тогда ваша отсортированная коллекция больше не будет отсортирована должным образом. То же самое и со словарями и хешами. Невероятный Эрик Липперт ( блог ) написал целую серию статей о неизменности и о том, почему он считает, что это будущее C #. Вот один из его примеров , который позволяет вам «изменять» переменную, доступную только для чтения.


ОБНОВЛЕНИЕ: ваш пример с:

this.readOnlyPoint.Offset(3, 4); // Is still (1, 2).

- это именно то, что Липперт упоминал в своем сообщении об изменении режима только для чтения. переменные. Смещение (3,4) фактически изменило точку , но это была копия readOnlyPoint , и она никогда не была назначена ни на что, поэтому потеряна.

И , что - вот почему изменяемые типы значений злы : Они позволяют вам думать , что вы что-то модифицируете, хотя иногда вы фактически изменяете копию, что приводит к неожиданным ошибкам. Если бы Point был неизменным, Offset () должен был бы вернуть новую Point , и вы не смогли бы назначить его readOnlyPoint . А затем вы говорите «Да, он доступен только для чтения по какой-то причине. Почему я пытался его изменить? Хорошо, что компилятор остановил меня сейчас».


ОБНОВЛЕНИЕ: Насчет вашего перефразированного запроса ... Я думаю Я знаю, к чему вы клоните. В каком-то смысле вы можете "думать" структур как внутренне неизменяемых, то изменение структуры аналогично замене ее измененной копией. Насколько мне известно, это может быть даже то, что CLR делает внутри памяти. (Так работает флэш-память. Вы не можете редактировать только несколько байтов, вам нужно прочитать целый блок килобайт в память, изменить несколько, которые вы хотите, и записать весь блок обратно.) Однако, даже если они были «внутренне неизменяемыми» ", это деталь реализации, и для нас, разработчиков, как пользователей структур (их интерфейса или API, если хотите), они могут быть изменены. Мы не можем игнорировать этот факт и «думать о них как о неизменяемых».

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

interface IFoo { DoStuff(); }
struct Foo : IFoo { /* ... */ }

IFoo otherFoo = new Foo();
IFoo foo = otherFoo;
foo.DoStuff(whatEverArgumentsYouLike); // line #1
foo = foo.DoStuff(whatEverArgumentsYouLike); // line #2

Строки №1 и №2 не имеют одинаковых результатов ... Почему? Поскольку foo и otherFoo относятся к тому же упакованному экземпляру Foo. Все, что было изменено в foo в строке №1, отражается в otherFoo . Строка № 2 заменяет foo новым значением и ничего не делает с otherFoo (при условии, что DoStuff () возвращает новый экземпляр IFoo и не изменяет сам foo .

Foo foo1 = new Foo(); // creates first instance
Foo foo2 = foo1; // create a copy (2nd instance)
IFoo foo3 = foo2; // no copy here! foo2 and foo3 refer to same instance

Изменение foo1 не влияет на foo2 или foo3 . Изменение foo2 отразится в foo3 , но не в foo1 . Изменение foo3 отразится в foo2 , но не в foo1 .

Смущает? Придерживайтесь неизменяемых типов значений, и вы избавитесь от необходимости изменять любой из них.


ОБНОВЛЕНИЕ: исправлена ​​опечатка в первом примере кода

45
ответ дан 27 November 2019 в 01:30
поделиться

Изменяемость и типы значений - это две разные вещи.

Определение типа как типа значения указывает, что среда выполнения будет копировать значения вместо ссылки на среду выполнения. С другой стороны, изменчивость зависит от реализации, и каждый класс может реализовать ее по своему усмотрению.

10
ответ дан 27 November 2019 в 01:30
поделиться

Не хочу усложнять рассуждения об этом, учитывая ссылку параметры и бокс. Я также в курсе что p = p.Offset (3, 4); выражает неизменность намного лучше, чем p.Offset (3, 4); делает. Но остается вопрос - не типы значений неизменный по определению?

Что ж, тогда вы действительно не работаете в реальном мире, не так ли? На практике склонность типов значений создавать копии самих себя при перемещении между функциями хорошо сочетается с неизменяемостью, но на самом деле они не являются неизменяемыми, если вы не сделаете их неизменяемыми, поскольку, как вы указали, вы можете использовать ссылки на них просто как и все остальное.

4
ответ дан 27 November 2019 в 01:30
поделиться

Вы можете писать изменяемые структуры, но лучше всего сделать типы значений неизменяемыми.

Например, DateTime всегда создает новые экземпляры при выполнении любой операции. Point является изменяемым и может быть изменено.

Отвечу на ваш вопрос: Нет, они не являются неизменяемыми по определению, это зависит от случая, должны они быть изменяемыми или нет. Например, если они должны служить ключами словаря, они должны быть неизменными.

7
ответ дан 27 November 2019 в 01:30
поделиться

не являются типами значений неизменяемыми по определению. ?

Нет, это не так: если вы посмотрите, например, на структуру System.Drawing.Point , у нее есть сеттер, а также геттер в свойстве X .

Однако может быть верно сказать, что все типы значений должны быть определены с помощью неизменяемых API.

4
ответ дан 27 November 2019 в 01:30
поделиться

Если вы углубитесь в свою логику, то все типы являются неизменяемыми. Когда вы изменяете ссылочный тип, вы можете утверждать, что вы действительно пишете новый объект по тому же адресу, а не изменяете что-либо.

Или вы можете утверждать, что все является изменяемым на любом языке, потому что иногда память, которая имела ранее использовался для одного, будет заменен другим.

Имея достаточно абстракций и игнорируя достаточное количество языковых функций, вы можете прийти к любому выводу, который вам нравится.

И это упускает суть. Согласно спецификации .NET, типы значений изменяемы. Вы можете изменить его.

int i = 0;
Console.WriteLine(i); // will print 0, so here, i is 0
++i;
Console.WriteLine(i); // will print 1, so here, i is 1

но он все тот же i. Переменная i объявляется только один раз. Все, что происходит с ним после этого объявления, является модификацией.

В чем-то вроде функционального языка с неизменяемыми переменными это было бы незаконно. ++ i было бы невозможно. После объявления переменной она имеет фиксированное значение.

В .NET это не так, мне ничто не мешает изменить i после того, как она была объявлена.

Поразмыслив немного над этим, вот еще один пример, который может быть лучше:

struct S {
  public S(int i) { this.i = i == 43 ? 0 : i; }
  private int i;
  public void set(int i) { 
    Console.WriteLine("Hello World");
    this.i = i;
  }
}

void Foo {
  var s = new S(42); // Create an instance of S, internally storing the value 42
  s.set(43); // What happens here?
}

В последней строке, согласно вашей логике, мы могли бы сказать, что мы фактически создаем новый объект и перезаписываем старый с этим значением. Но это невозможно! Чтобы создать новый объект, компилятор должен установить для переменной i значение 42. Но это закрыто! Он доступен только через определяемый пользователем конструктор, который явно запрещает значение 43 (вместо этого устанавливая его на 0), а затем через наш метод set , который имеет неприятный побочный эффект. У компилятора нет способа просто создать новый объект с теми значениями, которые ему нравятся. Единственный способ установить si на 43 - это изменить текущий объект, вызвав set () . Компилятор не может просто сделать это, потому что он изменит поведение программы (она будет выводить на консоль)

Таким образом, для того, чтобы все структуры были неизменными, компилятор должен был обмануть и нарушить правила языка , И конечно, если мы Если хочешь нарушить правила, мы можем доказать что угодно. Я мог бы доказать, что все целые числа тоже равны или что определение нового класса приведет к возгоранию вашего компьютера. Пока мы остаемся в рамках правил языка, структуры изменчивы.

5
ответ дан 27 November 2019 в 01:30
поделиться

I think the confusion is that if you have a reference type that should act like a value type it's a good idea to make it immutable. One of the key differences between value types and reference types is that a change made through one name on a ref type can show up in the other name. This doesn't happen with value types:

public class foo
{
    public int x;
}

public struct bar
{
    public int x;
}


public class MyClass
{
    public static void Main()
    {
        foo a = new foo();
        bar b = new bar();

        a.x = 1;
        b.x = 1;

        foo a2 = a;
        bar b2 = b;

        a.x = 2;
        b.x = 2;

        Console.WriteLine( "a2.x == {0}", a2.x);
        Console.WriteLine( "b2.x == {0}", b2.x);
    }
}

Produces:

a2.x == 2
b2.x == 1

Now, if you have a type that you'd like to have value semantics, but don't want to actually make it a value type - maybe because the storage it requires is too much or whatever, you should consider that immutability is part of the design. With an immutable ref type, any change made to an existing reference produces a new object instead of change the existing one, so you get the value type's behavior that whatever value you're holding cannot be changed through some other name.

Of course the System.String class is a prime example of such behavior.

2
ответ дан 27 November 2019 в 01:30
поделиться

Нет, это не так. Пример:

Point p = new Point (3,4);
Point p2 = p;
p.moveTo (5,7);

В этом примере moveTo () является операцией на месте . Это меняет структуру, которая скрывается за ссылкой p . Вы можете увидеть это, посмотрев на p2 : его положение также изменится. С неизменяемыми структурами moveTo () должен будет возвращать новую структуру:

p = p.moveTo (5,7);

Теперь Point неизменяем, и когда вы создаете ссылку на нее в любом месте вашего кода, вы выигрываете Никаких сюрпризов. Давайте посмотрим на i :

int i = 5;
int j = i;
i = 1;

Это другое. i не является неизменным, 5 является. И второе присваивание не копирует ссылку на структуру, содержащую i , а копирует содержимое i . Итак, за кулисами, происходит нечто совершенно иное: вы получаете полную копию переменной, а не только копию адреса в памяти (ссылка).

Эквивалентом объектов может быть конструктор копии:

Point p = new Point (3,4);
Point p2 = new Point (p);

Здесь внутренняя структура p копируется в новый объект / структуру, а p2 будет содержать ссылку на него. Но это довольно дорогостоящая операция (в отличие от целочисленного присваивания выше), поэтому большинство языков программирования делают различие.

По мере того, как компьютеры становятся более мощными и получают больше памяти, это различие исчезнет, ​​потому что оно вызывает огромное количество ошибок и проблем. В следующем поколении будут только неизменяемые объекты, любая операция будет защищена транзакцией, и даже int будет полноценным объектом. Так же, как и сборка мусора, это будет большим шагом вперед в стабильности программы, вызовет много проблем в первые несколько лет, но позволит писать надежное программное обеспечение. Сегодня компьютеры для этого недостаточно быстры.

1
ответ дан 27 November 2019 в 01:30
поделиться

Нет, типы значений не неизменяемы по определению

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

struct MutableStruct
{
    private int state;

    public MutableStruct(int state) { this.state = state; }

    public void ChangeState() { this.state++; }
}

struct ImmutableStruct
{
    private readonly int state;

    public MutableStruct(int state) { this.state = state; }

    public ImmutableStruct ChangeState()
    {
        return new ImmutableStruct(this.state + 1);
    }
}

[Продолжение следует ...]

1
ответ дан 27 November 2019 в 01:30
поделиться

Объекты / структуры неизменяемы, когда они передаются в функцию таким образом, как данные не могут быть измененным, и возвращенная структура является структурой новая . Классический пример:

String s = "abc";

s.toLower ();

, если функция toLower записана так, что возвращается новая строка, заменяющая "s" , это неизменный,

0
ответ дан 27 November 2019 в 01:30
поделиться

В прошлом году я написал в блоге сообщение о проблемах, с которыми вы можете столкнуться, не создавая структуры неизменяемый.

Полную версию сообщения можно прочитать здесь

Это пример того, как что-то может пойти не так:

//Struct declaration:

struct MyStruct
{
  public int Value = 0;

  public void Update(int i) { Value = i; }
}

Пример кода:

MyStruct[] list = new MyStruct[5];

for (int i=0;i<5;i++)
  Console.Write(list[i].Value + " ");
Console.WriteLine();

for (int i=0;i<5;i++)
  list[i].Update(i+1);

for (int i=0;i<5;i++)
  Console.Write(list[i].Value + " ");
Console.WriteLine();

Вывод этого кода:

0 0 0 0 0
1 2 3 4 5

Теперь давайте сделаем то же самое, но заменить массив на общий List <> :

List<MyStruct> list = new List<MyStruct>(new MyStruct[5]); 

for (int i=0;i<5;i++)
  Console.Write(list[i].Value + " ");
Console.WriteLine();

for (int i=0;i<5;i++)
  list[i].Update(i+1);

for (int i=0;i<5;i++)
  Console.Write(list[i].Value + " ");
Console.WriteLine();

Результат:

0 0 0 0 0
0 0 0 0 0

Объяснение очень простое. Нет, это не упаковка / распаковка ...

При доступе к элементам из массива среда выполнения получает элементы массива напрямую, поэтому метод Update () работает с самим элементом массива. Это означает, что обновляются сами структуры в массиве.

Во втором примере мы использовали общий List <> . Что происходит, когда мы получаем доступ к определенному элементу? Итак, вызывается свойство indexer, которое является методом. Типы значений всегда копируются при возврате методом, поэтому происходит именно это: список ' Метод индексатора s извлекает структуру из внутреннего массива и возвращает ее вызывающей стороне. Поскольку это относится к типу значения, будет сделана копия, и для копии будет вызван метод Update (), который, конечно же, не влияет на исходные элементы списка.

Другими словами, всегда проверяйте свои структуры неизменны, потому что вы никогда не знаете, когда будет сделана копия. В большинстве случаев это очевидно, но в некоторых случаях может действительно вас удивить ...

2
ответ дан 27 November 2019 в 01:30
поделиться
Другие вопросы по тегам:

Похожие вопросы: