繁体   English   中英

C# - 在 TextBox 上使用 Async 和 Await 的跨线程错误

[英]C# - Cross Thread Error using Async and Await on TextBox

我是 Async 和 Await 的新手,并创建了一个简单的项目来了解它是如何工作的。 为此,我有一个简单的 Windows 窗体应用程序,它有 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 和 Await 的实现和理解是错误的吗? 是否可以在 Windows 窗体应用程序中使用 Async 和 Await?

您无法访问不同线程上的 UI 线程。 这应该有帮助

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";

}

当我读到 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 as from 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.Run的用途:

将指定的工作排入队列以在 ThreadPool 上运行

换句话说,它将您作为委托传递给线程池线程的任何内容推送 由于我们在 WinForms 中,这意味着匿名方法() => GetCompletedItems(queueSystem)将在线程池线程中执行,而不是在 UI 线程中执行。

这是上面的代码示例,几乎没有变化:

    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);
    }

您可以通过以下方式从另一个线程访问该线程。 它确实有助于避免应用程序中的跨线程异常。

private void Thread()
{
    this.Invoke((System.Action)(() => {
        //your thread call or definition
    });
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM