简体   繁体   中英

Why do I have to use (async method).result instead of await (async method)?

I am starting 2 channels in the mediaservices azure portal. Starting a channel takes a long time to complete, about 25-30 seconds per channel. Hence, multithreading :)

However, the following is not clear to me:

I have 2 methods:

public async Task<bool> StartAsync(string programName, CancellationToken token = default(CancellationToken))
    {
        var workerThreads = new List<Thread>();
        var results = new List<bool>();

        foreach (var azureProgram in _accounts.GetPrograms(programName))
        {
            var thread = new Thread(() =>
            {
                var result = StartChannelAsync(azureProgram).Result;
                lock (results)
                {
                    results.Add(result);
                }
            });
            workerThreads.Add(thread);
            thread.Start();
        }

        foreach (var thread in workerThreads)
        {
            thread.Join();
        }

        return results.All(r => r);
    }

and

private async Task<bool> StartChannelAsync(IProgram azureProgram)
    {
        var state = _channelFactory.ConvertToState(azureProgram.Channel.State);
        if (state == State.Running)
        {
            return true;
        }

        if (state.IsTransitioning())
        {
            return false;
        }

        await azureProgram.Channel.StartAsync();

        return true;
    }

in the first method I use

var result = StartChannelAsync(azureProgram).Result;

In this case everything works fine. But if I use

var result = await StartChannelAsync(azureProgram);

Executing is not awaited and my results has zero entries. What am I missing here?

And is this a correct way?

Any comments on the code is appreciated. I am not a multithreading king ;)

Cheers!

Don't span new Thread instances to execute tasks in parallel, instead use Task.WhenAll :

public async Task<bool> StartAsync(string programName, CancellationToken token = default(CancellationToken))
{
    // Create a task for each program and fire them "at the same time"
    Task<bool>[] startingChannels = _accounts.GetPrograms(programName))
                                                        .Select(n => StartChannelAsync(n))
                                                        .ToArray();

    // Create a task that will be completed when all the supplied tasks are done
    bool[] results = await Task.WhenAll(startingChannels);

    return results.All(r => r);
}

Note: I see that you're passing a CancellationToken to your StartAsync method, but you're not actually using it. Consider passing it as an argument to StartChannelAsync, and then use it when calling azureProgram.Channel.StartAsync


If you love one-liners:

public async Task<bool> StartAsync(string programName, CancellationToken token = default(CancellationToken))
{
     return (await Task.WhenAll(_accounts.GetPrograms(programName)
                                         .Select(p => StartChannelAsync(p))
                                         .ToArray())).All(r => r);
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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