rena
rena

Reputation: 1278

c# wait for a task inside a loop

I'm using TPL in a Windows Form Application in the following way:

foreach (var item in items)
{
    task = Task<object>.Factory.StartNew (() => doWork());
    task.Wait();
    //update the UI using the result
}

I am waiting for the task to finish, because I need to process every item in the list, but as you imagine this is causing a lock in my UI thread (the UI freezes).

I'd like to know how can I implement this in a way that does not lock the UI thread.

Update: I'm using .NET Framework 4.5

Thanks.

P.S DoWork() is a long run operation.

Upvotes: 1

Views: 4946

Answers (2)

Douglas
Douglas

Reputation: 54887

Make your event handler async, and call await on the task representing the thread pool operation. This will cause your event handler to relinquish control over the UI thread until the task completes, then resume to update the UI and start the next task.

private async void MyButton_Click(object sender, EventArgs e)
{
    foreach (var item in items)
    {
        // assuming C# 5 closure semantics
        await Task.Run(() => doWork(item));

        // update the UI using the result
    }
}

The above approach assumes that you want to run your tasks sequentially. If you want to launch them in parallel and process their results in order of completion, you could use:

private async void MyButton_Click(object sender, EventArgs e)
{
    var tasks = items.Select(item => Task.Run(() => doWork(item))).ToList();
    while (tasks.Any())
    {
        var task = await Task.WhenAny(tasks);
        tasks.Remove(task);
        var result = await task;

        // update the UI using the result
    }
}

Upvotes: 2

Yuval Itzchakov
Yuval Itzchakov

Reputation: 149538

You can go about this in a couple of ways.

One would be not to execute each item in your list on a thread pool thread, but queue the entire foreach loop inside a Task and then update the results when they finish.

public async void SoneEventHandler(object sender, EventArgs e)
{
    var result = await Task.Run(() => items.Select(item => DoWork()).ToList());
    foreach (var item in items)
    {
        // Update UI
    }
}

If you still want to process each item on a threadpool threas, use Task.Run and await on the Task, don't block with Task.Wait():

public async void SoneEventHandler(object sender, EventArgs e)
{
    foreach (var item in items)
    {
        var result = await Task.Run(() => DoWork());
        // Update UI
    }
}

Upvotes: 3

Related Questions