Доступ к control-у не из того потока, в котором он был создан без Invoke

90
18 января 2022, 17:30

Раньше я часто использовал Invoke для решения подобных проблем, и проблем никогда не было. Но слышал, что с появлением async/await эта проблема решена. В частности, натыкаюсь на такие ответы, где автор первого ответа пишет, что для решения, начиная с .NET 4.0 можно "Использовать Асинхронную модель на основе задач (TAP) и ключевые слова async-await".

Сейчас я использую .NET Framework 4.5.2 и VS2015, и пытаюсь следовать его совету:

     private async void startButton_Click(object sender, EventArgs e){
          await Task.Run(() => Scope_Save(sourceText.Text, folderPath, statusView, ref flag));
     }
    void Scope_Save(string source, string path, RichTextBox logText, ref bool? _flag)
    {
          logText.Text += Environment.NewLine + "Читаем построчно:";
    }

Здесь sourceText и statusView - это RichTextBox-ы. В принципе от первого легко избавиться на обычный текст, но не суть. При первом обращении к logText, я получаю ошибку из сабжа. Что я делаю не так?

Answer 1

async/await не решают проблемы доступа к контролам из другого потока (т.к. WinForms строго это проверяет). Поэтому и доступ в Task.Run не возможет.

Но async/await значительно упрощает написание кода, который работает в разных потоках. Скорее всего проще всего это будет понять из примера из другого ответа.

private async void button1_Click(object sender, EventArgs e)
{
    // тут вы статруете таск и он запускается в отдельном потоке
    // с помощью await вы дожидаетесь результата
    // но работы с UI в отдельном потоке нет, т.к. как и раньше будут бросаться исключения
    string result = await Task.Factory.StartNew<string>(
                                             () => Worker.SomeLongOperation(),  
                                             TaskCreationOptions.LongRunning);
    // этот код выполняется только после завершения потока, за что и отвечает await
    // но для WinForms тут есть ещё один важный момент, это то, что этот код будет выполняться в UI потоке, т.е. уже никаких исключений не будет
    // это все делается при помощи async/await и SynchronizationContext, статей в интернете достаточно, если интересно почитайте.
    this.label1.Text = result;
}

И сравните его с вашим примером, в котором вы напрямую пытаетесь получить доступ из отдельного потока, поэтому и исключение. async/await не прямая замена Invoke.

Answer 2

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

// если весь метод вызывается из UI потока, то
private async void startButton_Click(object sender, EventArgs e){
    // тут можно работать с UI элементами
    await Task.Run(() => // тут НЕЛЬЗЯ работать с UI элементами т.к. это не UI поток);
    // тут можно работать с UI элементами
}
READ ALSO
Добавление кнопки с ссылкой

Добавление кнопки с ссылкой

Коллеги, доброго времени сутокУ меня меня есть страница формата "

80
С# задать значение переменной - вызвать метод

С# задать значение переменной - вызвать метод

Добрый день дамы и господа) набросал тут вот такой код:

90