C # - Ошибка перекрестного потока с использованием асинхронного и Await на TextBox - PullRequest
0 голосов
/ 23 ноября 2018

Я новичок в Async и Await и создал простой проект, чтобы понять, как он работает.Для этого у меня есть простое приложение Windows Form, которое имеет 2 элемента:

  • Кнопка Получить завершенные элементы
  • Текстовое поле, отображающее все полученные завершенные элементы

КогдаЯ нажимаю кнопку, она должна отображать все завершенные элементы в TextBox.Это код, который я написал:

private async void btnGetCompletedItems_Click(object sender, EventArgs e)
{
    QueueSystem queueSystem = QueueSystem.NewInstance(75);

    Stopwatch watch = new Stopwatch();

    watch.Start();
    await Task.Run(() => GetCompletedItems(queueSystem));
    watch.Stop();

    lblTime.Text = $"{watch.ElapsedMilliseconds.ToString()} ms";

}

private void GetCompletedItems(QueueSystem queueSystem)
{
    foreach (var item in queueSystem.GetCompletedItems())
    {
        txtItems.Text += $"{txtItems.Text}{item.ItemKey}{Environment.NewLine}";
    }
}

Однако я получаю ошибку в

txtItems.Text + = $ "{txtItems.Text} {item.ItemKey} {Environment.NewLine} ";

В сообщении об ошибке указывается

Дополнительная информация: операция между потоками недопустима: элемент управления 'txtItems' доступен из потока, отличного от потокапоток, в котором он был создан.

Я зарегистрировался в Debug, и был создан новый поток для GetCompletedItems ().Когда я читал об Async и Await, я читал, что он не обязательно создает новый поток, но, кажется, по какой-то причине он создал новый.

Является ли моя реализация и понимание Async иЖдешь неправильно?Можно ли использовать Async и Await в приложении Windows Forms?

Ответы [ 3 ]

0 голосов
/ 23 ноября 2018

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

private void Thread()
{
    this.Invoke((System.Action)(() => {
        //your thread call or definition
    });
}
0 голосов
/ 23 ноября 2018

Когда я читаю об Async и Await, я читаю, что это не обязательно создает новый поток

Это верно для обычных асинхронных методов.Подумайте об этом:

    private async void button1_Click(object sender, EventArgs e)
    {
        Trace.WriteLine(Thread.CurrentThread.ManagedThreadId);

        await DoesNothing();
    }

    private async Task DoesNothing()
    {
        // outputs the same thread id as similar line afrom above;
        // in particlar, for WinForms this means, that at this point
        // we are still at UI thread
        Trace.WriteLine(Thread.CurrentThread.ManagedThreadId);

        await Task.Delay(1);
    }

, но, кажется, по какой-то причине он создал новую

Это то, для чего предназначен Task.Runfor:

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

Другими словами, толкает все, что вы передаете в качестве делегатапоток пула потоков.Поскольку мы находимся в WinForms, это означает, что анонимный метод () => GetCompletedItems(queueSystem) будет выполняться в потоке пула потоков, а не в пользовательском интерфейсе.

Ниже приведен пример кода с небольшими изменениями:

    private async void button1_Click(object sender, EventArgs e)
    {
        Trace.WriteLine(Thread.CurrentThread.ManagedThreadId);

        await Task.Run(DoesNothing);
    }

    private async Task DoesNothing()
    {
        // outputs DIFFERENT thread id;
        // in particlar, for WinForms this means, that at this point
        // we are not at UI thread, and we CANNOT access controls directly
        Trace.WriteLine(Thread.CurrentThread.ManagedThreadId);

        await Task.Delay(1);
    }
0 голосов
/ 23 ноября 2018

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

private async void btnGetCompletedItems_Click(object sender, EventArgs e)
{
    QueueSystem queueSystem = QueueSystem.NewInstance(75);

    Stopwatch watch = new Stopwatch();

    watch.Start();
    var results = await Task.Run(() => queueSystem.GetCompletedItems());
    foreach (var item in results)
    {
        txtItems.Text += $"{txtItems.Text}{item.ItemKey}{Environment.NewLine}";
    }
    watch.Stop();

    lblTime.Text = $"{watch.ElapsedMilliseconds.ToString()} ms";

}
...