thecodeexplorer
thecodeexplorer

Reputation: 373

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

I am new to Async and Await and have created a simple project in order to understand how it works. For this, I have a simple Windows Form application that has 2 elements:

When I click the button, it should display all completed Items in the TextBox. This is the code I have written:

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

However, I am getting an error in

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

The error says

Additional information: Cross-thread operation not valid: Control 'txtItems' accessed from a thread other than the thread it was created on.

I checked in Debug and a new thread was created for GetCompletedItems(). When I read about Async and Await, I read that it doesn't necessarily create a new thread but it seems to have created a new one for some reason.

Is my implementation and understanding of Async and Await wrong? Is it possible to use Async and Await in a Windows Forms application?

Upvotes: 7

Views: 5351

Answers (3)

Dennis
Dennis

Reputation: 37770

When I read about Async and Await, I read that it doesn't necessarily create a new thread

This is true for regular async methods. Consider this:

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

but it seems to have created a new one for some reason

This is what Task.Run is intended for:

Queues the specified work to run on the ThreadPool

In other words, it pushes anything you pass it as a delegate to a thread pool thread. Since we are in WinForms, this means, that anonymous method () => GetCompletedItems(queueSystem) will be executed at thread pool thread, not at UI one.

Here's code sample from above with little change:

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

Upvotes: 3

Ajar
Ajar

Reputation: 162

You can access the thread from another thread in a following way. It does helps to avoid the cross thread exception in your application.

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

Upvotes: 2

Elwick
Elwick

Reputation: 126

You cannot access UI thread on a different thread. This should help

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

}

Upvotes: 8

Related Questions