async/await continuation

use*_*342 6 c# asynchronous async-await

在下面的代码中,我需要并行执行三个Get ...方法.当Get ...方法完成时,我需要立即调用Save ...方法.注意保存...将事物作为参数.在DoStuffAsync返回之前,必须完成所有Get和Save方法.

我的猜测是我需要继续使用Get ...方法,但我不知道如何构建它.

protected async void DoStuffAsync()
{
    SomeThing thing = new SomeThing { ID = 5 };
    SomeRepository rep = new SomeRepository();

    // We need to run all three Get... methods in parallel
    // As soon as a Get... method completes we need to save the result to the correct property on thing and call the Save... method .

    var getRed = rep.GetRedAsync().ContinueWith<Task<string>>(async x => { thing.Color1 = x.Result; await rep.SaveRedAsync(thing); return x; }); // does not compile
    var getBlue = rep.GetBlueAsync();
    var getGreen = rep.GetGreenAsync();

    string red = await getRed.Result; // this is not good because getBlue may finish before getRed.  We want dont want to wait on getRed before calling SaveBlue
    await rep.SaveRedAsync(thing);
    var b = await getBlue;  
    var c = await getGreen;

    // thing must be fully initialized before DoStuffAsync returns

    }


public class SomeThing
{
    public int ID { get; set; }
    public string Color1 { get; set; }
    public string Color2 { get; set; }
    public string Color3 { get; set; }
}

public class SomeRepository
{
    public async Task<string> GetRedAsync()
    {
        return await Task.Run(() => "red");
    }

    public async Task<string> GetBlueAsync()
    {
        return await Task.Run(() => "blue");
    }

    public async Task<string> GetGreenAsync()
    {
        return await Task.Run(() => "green");
    }

    public async Task SaveRedAsync(SomeThing thing)
    {
        // We need thing.ID here as well as other properties
        await Task.Delay(1);
    }

    public async Task SaveBlueAsync(SomeThing thing)
    {
        await Task.Delay(1);
    }

    public async Task SaveGreenAsync(SomeThing thing)
    {
        await Task.Delay(1);
    }
}
Run Code Online (Sandbox Code Playgroud)

Jon*_*eet 7

好吧,你可以明确地使用ContinueWith- 或者你可以将每个"get and save"分解为单独的异步方法或异步lambda.例如:

async Task GetAndSaveRedAsync(SomeThing thing, SomeRepository rep)
{
    var red = await rep.GetRedAsync();
    thing.Red = red;
    await SaveRedAsync(red);
    // Return red if you want (change the return type to Task<string>)
}

// Ditto for the others
Run Code Online (Sandbox Code Playgroud)

然后:

protected async void DoStuffAsync()
{
    SomeThing thing = new SomeThing { ID = 5 };
    SomeRepository rep = new SomeRepository();

    var handleRed = GetAndSaveRedAsync(thing, rep);
    var handleBlue = GetAndSaveBlueAsync(thing, rep);
    var handleYellow = GetAndSaveYellowAsync(thing, rep);

    // Or use Task.WhenAll
    await handleRed;
    await handleBlue;
    await handleYellow;
}
Run Code Online (Sandbox Code Playgroud)

  • @RandRandom:是什么让你认为任何东西都在不同的线程上运行?是的,您*可以*使用`Task.Run`并为此专门设置一个线程,但为什么要创建比您需要的更多的线程? (4认同)
  • @RandRandom:不一定,没有.它*可能*做,但它当然不必.考虑一个WinForms应用程序,它包含`async void HandleButtonClick(...){button.Text ="Waiting"; 等待Task.Delay(1000); button.Text ="等待"; 那里没有额外的线程.你可能值得再次阅读async :) (4认同)
  • @ user579342:没有"线程内部的线程"这样的东西,我给出的代码*根本不会*启动任何新线程.我没有回答RandRandom的问题,而是质疑其基本假设.你似乎在假设TAP是关于并行性的 - 它不是,它是关于异步的,这是不同的. (3认同)

nos*_*tio 7

我不会混合ContinueWith,await而是async直接使用lambda:

protected async Task  DoStuffAsync()
{
    SomeThing thing = new SomeThing { ID = 5 };
    SomeRepository rep = new SomeRepository();

    // We need to run all three Get... methods in parallel
    // As soon as a Get... method completes we need to save the result to the correct property on thing and call the Save... method .

    Func<Task<X>> getRedFunc = async() =>
    {
        var result = await rep.GetRedAsync();
        thing.Color1 = result;
        await rep.SaveRedAsync(thing);
        return result;
    };

    var getRed = getRedFunc();

    var getBlue = rep.GetBlueAsync();
    var getGreen = rep.GetGreenAsync();

    await Task.WhenAll(getRed, getBlue, getGreen);
}
Run Code Online (Sandbox Code Playgroud)

此外,async void除了事件处理程序之外,不要使用任何方法.您将无法观察到这样的方法的完成或处理可能在其中抛出的异常.