async / await ведет себя странно из-за перезапуска уже выполненного кода - PullRequest
0 голосов
/ 01 августа 2020

Я наблюдаю странное поведение, связанное с async / await, но не могу его объяснить.

Вот основные c logi c:

public async Task Init(UI.INavigable NavigateFrom) <-- Starts from here
{
    await NavigateFrom.Navigate<UI.Pages.Page>(await Common.CreateDrillInNavigationTransitionInfo(), async (Page) =>
    {
        // ...
        m_SelectPage.OnCategorySelected += OnCategorySelected;
        // ... (including more awaits)
        await OnModelUpdated();
        return Page;
    });
}

private async Task OnModelUpdated()
{
    await m_SelectPage.DataUpdated(...);
}

public async Task DataUpdated(...)
{
    try
    {
        m_Initializing = true;
        await Common.RunUiTask(async () =>
        {
            // ... (including more awaits)
            await Task.Run(async () =>
            {
                // ... (including more awaits)
                System.Diagnostics.Debug.Print("DataUpdated: Calling CategorySelected\n");
                await OnCategorySelected(await GetSelectedCategory());
            });
        });
    }
    finally
    {
        m_Initializing = false;
    }
}
private async Task OnCategorySelected(Model.Category Category)
{
    Debug.Print("OnCategorySelected start\n");
    var Adaptor = new Model.Adaptor(...);
    var p = new InfoPage(m_ActualApp, Adaptor);
    Debug.Print("OnCategorySelected navigate\n");
    await p.Init(SelectPage);
    Debug.Print("OnCategorySelected end\n");
}

public async Task Init(UI.INavigable NavigateFrom)
{
    Debug.Print("Init start\n");
    await NavigateFrom.Navigate<UI.Pages.InfoPage>(await Common.CreateDrillInNavigationTransitionInfo(), async (Page) =>
    {
        Debug.Print("Init lambda\n");
        // ...
        Debug.Print("Init lambda update\n");
        await m_InfoPage.Updated(...);
        Debug.Print("Init lambda end\n");
        return Page;
    });
    Debug.Print("Init end\n");
}

И вот результат одного прогона (он не каждый раз ведет себя одинаково):

DataUpdated: Calling CategorySelected
OnCategorySelected start
OnCategorySelected navigate
Init start
OnCategorySelected start
OnCategorySelected navigate
Init start
Init lambda
Init lambda update
OnCategorySelected start
OnCategorySelected navigate
Init start
Init lambda end
Init end
OnCategorySelected end
Init lambda
Init lambda update
Init lambda
Init lambda update
Init lambda end
Init end
OnCategorySelected end
Init lambda end
Init end
OnCategorySelected end

Как ясно видно из вывода, кажется, что как только он попадает в await в Init или он попадает в await внутри лямбды в функции Init, он приостанавливается (что правильно), но затем перезапускается обратно в функции OnCategorySelected без видимой причины.

Я не могу объяснить, что происходит. Это должно произойти только один раз, поскольку нет кода, который снова вызывает OnCategorySelected. Есть идеи?

Для справки, вот служебные функции, которые я использую на случай, если в них есть какие-то странности:

public static async Task<DrillInNavigationTransitionInfo> CreateDrillInNavigationTransitionInfo()
{
    return await Common.RunUiTask(async () =>
    {
        return await Task.FromResult(new DrillInNavigationTransitionInfo());
    });
}
    
public static class Common
{
    public static async Task<T> RunUiTask<T>(Func<Task<T>> F)
    {
        return await CoreApplication.MainView.CoreWindow.Dispatcher.RunTaskAsync(async () =>
        {
            return await F();
        });
    }       
}

public static class DispatcherTaskExtensions
{
    public static async Task<T> RunTaskAsync<T>(this CoreDispatcher Dispatcher, CoreDispatcherPriority Priority, Func<Task<T>> Func)
    {
        var TaskCompletionSource = new TaskCompletionSource<T>();
        await Dispatcher.RunAsync(Priority, async () =>
        {
            try
            {
                var R = await Func();
                TaskCompletionSource.SetResult(R);
            }
            catch (Exception ex)
            {
                TaskCompletionSource.SetException(ex);
            }
        });
        return await TaskCompletionSource.Task;
    }

    public static async Task<T> RunTaskAsync<T>(this CoreDispatcher Dispatcher, Func<Task<T>> Func)
    {
        return await RunTaskAsync(Dispatcher, CoreDispatcherPriority.Normal, Func);
    }
}

1 Ответ

0 голосов
/ 01 августа 2020

Как я вижу, все ок. Asyn c означает асинхронный, если вы не знаете. Вы вызывали OnCategorySelected 3 раза, и он завершился 3 раза (вы можете увидеть это в журнале ur). Если вам нужен asyn c, но иногда необходимо синхронное завершение, вы можете использовать для этого SemaphoreSlim.

SemaphoreSlim sl = new SemaphoreSlim(1, 1);
//some async code
await sl.WaitAsync();
//some sync code
sl.Release();
//again async code
...