TaskCompletionSource и не потокобезопасная библиотека

У меня есть приложение C# avalonia, использующее некоторую не потокобезопасную библиотеку через SDK, предоставленный разработчиком. В частности, это Windows Zoom SDK. Некоторые функции SDK основаны на шаблоне, управляемом событиями. После вызова методов SDK приложение должно дождаться поступления обратного вызова результата выполнения. Поэтому в приложении был применен асинхронный шаблон на основе задач с использованием TaskCompletionSource (см. код ниже).

После применения шаблона async/await в приложении SDK работает некорректно (детали). Однако этот вопрос не обсуждает работу с Zoom SDK. Вопрос в том, как использование паттерна async/await потенциально может привести к некорректному поведению какой-то не потокобезопасной библиотеки (или SDK)?

Метод оболочки SDK:

public async Task<bool> SdkMethodAAsync(string parameter)
{
    try
    {
        this.sdkService.SdkMethodA(parameter);

        this.tcs = new TaskCompletionSource<bool>();
        return await this.tcs.Task;
    }
    catch (Exception)
    {
        return false;
    }
    
    return false;
    }

Обработчик обратного вызова SDK:

    public void OnMethodAReturn(MethodAResult ret)
    {
        // here some property can also be changed 
        // and which will trigger an event on which SDK calls can be made to         

        this.tcs.TrySetResult(ret == MethodAResult.METHODA_SUCCESS);
    }

Код высокого уровня:

    public async Task StartAsync(string parameter1, string parameter2)
    {
        var resultMethodA = await SdkMethodAAsync(parameter1);
        var resultMethodB = await SdkMethodBAsync(parameter2);
    }

person Aliaksei Luferau    schedule 19.05.2021    source источник
comment
В чем проблема? Вы видите, что SdkMethodBAsync вызывается не в том потоке?   -  person canton7    schedule 19.05.2021
comment
@canton7 Нет, все последующие вызовы выполняются в основном потоке. Эти вызовы (преобразованные в TAP) являются методами инициализации, аутентификации и входа в систему. Итак, в следующем SDK, похоже, находится в неправильном состоянии (неправильно обрабатываются пользователи, я получаю неожиданные обратные вызовы, некоторые методы выполняются неправильно). В версии приложения без ТАП все работает хорошо. Остальные программы идентичны. Но я также признаю, что TAP может быть и не проблемой. Возможно, мой вопрос больше теоретический. Какие подводные камни могут быть?   -  person Aliaksei Luferau    schedule 19.05.2021


Ответы (1)


Первая очевидная проблема заключается в том, что вы обновляете источник завершения задачи после вызова метода. Метод может завершиться синхронно, и это испортит ваш шаблон. Измените его на

    this.tcs = new TaskCompletionSource<bool>();
    this.sdkService.SdkMethodA(parameter);        
    return await this.tcs.Task;

Еще одна вещь, от которой вы, возможно, захотите защититься, — это одновременные вызовы. Скажите, что кто-то пытается сделать

    var mA = SdkMethodAAsync(parameter1);
    var mB = SdkMethodBAsync(parameter2);
    await Task.WhenAll(mA, mB);

Если SDK не разрешает одновременные вызовы, это будет ошибкой, но неясно, будет ли сообщаться об ошибке или она просто приведет к неожиданному поведению. Таким образом, вы можете захотеть создать какой-то механизм для обнаружения подобных случаев и каким-то образом обрабатывать их.

Еще одна вещь, которая может быть неочевидной

this.tcs.TrySetResult(ret == MethodAResult.METHODA_SUCCESS);

Это сделает задачу всегда успешной, а возвращаемое значение говорит о том, фактически она выполнена успешно или нет. Я ожидал, что задача будет переведена в успешное/неудачное/отмененное состояние в зависимости от результата метода. Это также может позволить возвращаемое значение или сообщения об ошибках вместо просто успешного/неудачного выполнения.

person JonasH    schedule 19.05.2021