0

我有以下代码,除了在 WhenAll ... await Task.WhenAll(syncTasks).ContinueWith ... 在所有四种方法完成之前运行之外,它似乎运行良好。如果我在这里做错了什么,将不胜感激。我真的不觉得我了解如何安排复杂的异步功能以及似乎正在发生的事情支持这一点。这是在 Xamarin App BTW 中,尽管我认为这并不重要。

private async Task SyncItems()
{
    var updateItemOnes = Task.Run(() => 
    {
        UpdateItemOnesToServer(itemOnesToUpdate).ContinueWith(async (result) => {
            if (!result.IsFaulted && !result.IsCanceled) 
            {
                await UpdateItemOnesToLocal(itemOnesToUpdate);  
            }
        });
    });

    syncTasks.Add(updateItemOnes);

    var updateItemTwos = Task.Run(() => 
    {
        UpdateItemTwosToServer(itemTwosToUpdate).ContinueWith(async (result) => {
            if (!result.IsFaulted && !result.IsCanceled) 
            {
                await UpdateItemTwosToLocal(itemTwosToUpdate);  
            }
        });
    });

    syncTasks.Add(updateItemTwos );

    //Show Loading Dialog

    await Task.WhenAll(syncTasks).ContinueWith((result) => {

        if (!result.IsFaulted && !result.IsCanceled)
        {
            //Success               
        }
        else
        {
            //Error
        }

        //Hide Loading Dialog
    });
}

private async Task UpdateItemOnesToServer(IEnumerable<Item> itemOnesToUpdate)
{
    try
    {
        var listofTasks = new List<Task>();

        foreach (var item in itemOnesToUpdate)
        {
            var convertItemOneTask = Task.Run(async () => {
               //Convert Image File in Item to Base64 here
            });

            listofTasks.Add(convertItemOneTask); 
        }

        await Task.WhenAll(listofTasks);

        var response = await _apiManager.SaveItemOnes(itemOnesToUpdate);

        if (response.IsSuccessStatusCode)
        {
            //Update ItemOnes for Local Update with Response Values
        }
    }
    catch
    {
        throw;
    }
}

private async Task UpdateItemOnesToLocal(IEnumerable<Item> itemOnesToUpdate)
{
    var listOfTasks = new List<Task<bool>>();

    foreach (var itemOne in itemOnesToUpdate)
    {
        listOfTasks.Add(_localService.UpdateItemOne(itemOne));
    }

    await Task.WhenAll<bool>(listOfTasks);    
}

private async Task UpdateItemTwosToServer(IEnumerable<ItemOne> itemTwosToUpdate)
{
    try
    {
        var listofTasks = new List<Task>();

        foreach (var item in itemTwosToUpdate)
        {
            var convertItemTwoTask = Task.Run(async () => {
               //Convert Image File in Item to Base64 here
            });

            listofTasks.Add(convertItemTwoTask); 
        }

        await Task.WhenAll(listofTasks);

        var response = await _apiManager.SaveItemTwos(itemTwosToUpdate);

        if (response.IsSuccessStatusCode)
        {
            //Update ItemTwos for Local Update with Response Values
        }
    }
    catch
    {
        throw;
    }

}

private async Task UpdateItemTwosToLocal(IEnumerable<ItemTwo> itemTwosToUpdate)
{
    var listOfTasks = new List<Task<bool>>();

    foreach (var itemTwo in itemTwosToUpdate)
    {
        listOfTasks.Add(_localService.UpdateItemTwo(itemTwo));
    }

    await Task.WhenAll<bool>(listOfTasks);    
}

提前感谢任何可以提供一点清晰的人。将不胜感激。

4

1 回答 1

1

所以这段代码有一些问题。

  1. someTask.ContinueWith(X) 基本上这说“一旦完成了一些任务,就做X”(还有更多的事情发生,但在这种情况下可以这样想)。但是,如果您等待someTask这将不包括该ContinueWith部分。所以像这样Task.WhenAll(syncTasks)不会等待你的ContinueWith部分。

  2. var updateItemOnes = Task.Run(() => UpdateItemOnesToServer())包装纸。这里没有等待,所以这将创建一个刚刚启动任务的UpdateItemOnesToServer任务。这是立即完成的。

如果您想查看实际发生的情况,请使用此测试类:

class TestAsyncClass
{
    public async Task Run()
    {
        var tasks = new List<Task>();
        Console.WriteLine("starting tasks");
        var task1 = Task.Run(() => {
            FakeServerCall1().ContinueWith(async (result) =>
                {
                    if (!result.IsFaulted && !result.IsCanceled)
                        await FakeLocalCall1();
                });
        });
        tasks.Add(task1);

        var task2 = Task.Run(() => {
            FakeServerCall2().ContinueWith(async (result) =>
            {
                if (result.IsCompletedSuccessfully)
                    await FakeLocalCall2();
            });
        }); 

        tasks.Add(task2);

        Console.WriteLine("starting tasks completed");

        await Task.WhenAll(tasks);

        Console.WriteLine("tasks completed");
    }

    public async Task<bool> FakeServerCall1()
    {
        Console.WriteLine("Server1 started");
        await Task.Delay(3000);
        Console.WriteLine("Server1 completed");
        return true;
    }

    public async Task<bool> FakeServerCall2()
    {
        Console.WriteLine("Server2 started");
        await Task.Delay(2000);
        Console.WriteLine("Server2 completed");
        return true;
    }

    public async Task<bool> FakeLocalCall1()
    {
        Console.WriteLine("Local1 started");
        await Task.Delay(1500);
        Console.WriteLine("Local1 completed");
        return true;
    }

    public async Task<bool> FakeLocalCall2()
    {
        Console.WriteLine("Local2 started");
        await Task.Delay(2000);
        Console.WriteLine("Local2 completed");
        return true;
    }
}

你会看到输出如下:

  • 开始任务
  • 开始任务完成
  • Server1 已启动
  • Server2 已启动
  • 完成的任务
  • Server2 完成
  • Local2 已启动
  • Server1 完成
  • Local1 开始
  • 本地2完成
  • 本地1已完成

注意这里的“任务完成”是在启动两个任务后直接调用的。

现在,如果我们像这样更改Run方法,我想我们将获得您正在寻找的功能:

public async Task Run()
{
    var tasks = new List<Task>();
    Console.WriteLine("starting tasks");
    var task1 = Task.Run(async () =>
    {
        await FakeServerCall1();
        await FakeLocalCall1();
    });
    tasks.Add(task1);

    var task2 = Task.Run(async() =>
    {
        await FakeServerCall2();
        await FakeLocalCall2();
    }); 

    tasks.Add(task2);

    Console.WriteLine("starting tasks completed");

    await Task.WhenAll(tasks);

    Console.WriteLine("tasks completed");
}

这将输出:

  • 开始任务
  • 开始任务完成
  • Server1 已启动
  • Server2 已启动
  • Server2 完成
  • Local2 已启动
  • Server1 完成
  • Local1 开始
  • 本地2完成
  • 本地1已完成
  • 完成的任务

所以我们在这里看到 Local1 总是在 Server1 之后,Local2 总是在 Server2 之后,“任务完成”总是在 Local1 和 Local2 之后

希望这可以帮助!

编辑:从您的评论中,您说您希望查看过程中发生的任何异常。这是您可以使用的地方ContinueWith(抛出异常时也会触发它:

await Task.WhenAll(tasks).ContinueWith((result) =>
{
    if (result.IsFaulted)
    {
        foreach (var e in result.Exception.InnerExceptions)
        {
            Console.WriteLine(e);
        }
    }
});

如果您更改以下测试调用:

public async Task<bool> FakeServerCall2()
{
    Console.WriteLine("Server2 started");
    await Task.Delay(1000);
    Console.WriteLine("Crashing Server2");
    throw new Exception("Oops server 2 crashed");
}

public async Task<bool> FakeLocalCall1()
{
    Console.WriteLine("Local1 started");
    await Task.Delay(1500);
    Console.WriteLine("crashing local1");
    throw new Exception("Oh ohh, local1 crashed");
}

这将是您的输出:

  • 开始任务
  • 开始任务完成
  • Server1 已启动
  • Server2 已启动
  • 崩溃的 Server2
  • Server1 完成
  • Local1 开始
  • 本地崩溃1
  • System.Exception:哦,哦,local1 在 ~\TestConsoleApp\TestConsoleApp\Program.cs: 第 67 行的 TestConsoleApp.TestAsyncClass.FakeLocalCall1() 处崩溃 ~\TestConsoleApp\TestConsoleApp\Program.cs:line 中的 TestConsoleApp.TestAsyncClass.b__0_0() 17
  • System.Exception:糟糕,服务器 2 在 ~\TestConsoleApp\TestConsoleApp\Program.cs: 第 59 行的 TestConsoleApp.TestAsyncClass.FakeServerCall2() 处崩溃,在 ~\TestConsoleApp\TestConsoleApp\Program.cs: 第 23 行的 TestConsoleApp.TestAsyncClass.b__0_1()
  • 完成的任务
于 2019-08-13T09:45:25.473 回答