Компилятор C # неверный асинхронный, ждите? - PullRequest
0 голосов
/ 09 сентября 2018

Я не задаю вопрос, но обсуждаю ошибку / ошибку в C # Compiler.

// do a heavy job it take 2s and return "1"
public async Task<string> DoJob() {
    var task = new Task<string>(() => {
        Thread.Sleep(2000);
        return "1";
    });
    task.Start();
    return await task;
}

private async void button1_Click(object sender, EventArgs e) {
    var task= DoJob();
    textBox1.Text += await task;
    this.Text += "2";
}

Когда я нажимаю на button1 3 раза, я ожидаю:

textBox1.Text == "111" 
this.Text == "222"

но результаты:

textBox1.Text == "1" 
this.Text == "222"

И еще одна ошибка , пока я жду 2 с (до 2 с), я изменяю textBox1.Text с помощью клавиатуры ввода, но получаю все тот же «1» вместо добавления в конец текста (оператор + =).


Основываясь на моих знаниях, async и await - ключевые слова ничего не делают, но помогают компилятору знать, куда поместить код в блок (поправьте меня):

Пример

ввод

private async void button1_Click(object sender, EventArgs e) {
    var task= DoJob();

    textBox1.Text += await task;
    this.Text += "2";
}

output : это дает ожидаемый результат, но отличается от C # Compiler. И это также не И еще одна ошибка выше.

private void button1_Click(object sender, EventArgs e) {
    var task= DoJob();

    task.ContinueWith((_task) => {
        this.Invoke(new Action(() => {
            textBox1.Text += _task.Result;
            this.Text += "2";
        }));
    });
}

но компилятор MS C # делает что-то подобное:

private void button1_Click(object sender, EventArgs e) {
    var task = DoJob();

    var left = textBox1.Text;
    task.ContinueWith((_task) => {                // textBox1.Text += await task;
        this.Invoke(new Action(() => {            //
            textBox1.Text = left + _task.Result;  // 
            this.Text += "2";                     // this.Text += "2";
        }));
    });

}

Считаете ли вы, что это ошибка или Microsoft намеренно это делает?

Ответы [ 3 ]

0 голосов
/ 09 сентября 2018

Рассмотрим эти два пути:

textBox1.Text += await task;

против

string newText = await task;
textBox1.Text += newText;

В первом примере текущее значение textBox1.Text равно читать сразу сразу всеми тремя щелчками, что, вероятно, является просто исходным значением пустого текстового поля. Затем, когда каждый из них завершается, он записывает исходное значение (как было, когда оно было прочитано, напоминание остается пустой строкой) плюс «1», который вы хотите объединить. Поэтому, если вы быстро нажмете кнопку, все три потока напишут значение "" + "1"

Во втором примере вы ожидаете получения результирующего текста, который хотите объединить, затем (в потоке пользовательского интерфейса) вы выполняете операцию + =, которая читает, объединяет и записывает без прерывания.

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

Имеет ли это смысл сейчас?

0 голосов
/ 09 сентября 2018

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

textBox1.Text += await task;

В C # нет специального оператора + =, он просто расширяется до:

textBox1.Text = textBox1.Text + await task;

Теперь, еслимы оцениваем слева направо, первое, что он должен оценить, это textBox1.set_Text для назначения.Если бы вы сделали что-то с правой стороны, которое изменило бы значение textBox1, вы все равно бы присвоили исходное textBox1, а не новое значение.

Следующее, что нужно оценить, это textBox1.get_Text,Это должно вернуть ваше значение "".

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

  1. Адрес объекта, в который мы собираемся записать.
  2. Строковое значение "".
  3. Строковое значение "1".

Осталось только добавить захваченное "" к ожидаемому "1" и присвоить его захваченному textBox1.

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

var temp = await task;
textBox1.Text += temp;

Это просто перемещает ожидание перед textBox1.Text.На самом деле вы можете сделать реорганизацию в строке и дать ожидаемые результаты, просто поменяв порядок выражений внутри оператора:

textBox1.Text = string.Concat(
    new[] { await task, textBox1.Text }.Reverse());
0 голосов
/ 09 сентября 2018

Здесь нет ошибки компилятора. Тем не менее, эта линия ваша проблема

textBox1.Text += await task;

Если вы изменили следующее, вы найдете последовательные результаты.

var result = await task;
textBox1.Text += result;

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

...