Используете TaskCompletionSource для преобразования библиотеки в ожидаемую? - PullRequest
0 голосов
/ 27 мая 2020

Я использую неасинхронную c USB-библиотеку от производителя USB-чипа, но хочу, чтобы она была асинхронной c, чтобы использование USB не блокировало пользовательский интерфейс. Я пытаюсь научиться программировать «правильный» путь, и, согласно моим исследованиям, для задач, связанных с вводом-выводом, таких как эта, мы хотим использовать TaskCompletionSource вместо Task.Run => (что больше подходит для задач, связанных с процессором). Учебники по TaskCompletionSource довольно скудны, а те, которые существуют, плохо объясняются для новичков.

После создания оболочек для библиотечных методов с помощью TaskCompletionSource я обнаружил, что пользовательский интерфейс все еще иногда блокируется (хотя он ведет себя намного лучше, чем без). Вот пример моего метода-оболочки:

public static Task<bool> SendMessageAsync(byte[] msg, int sizeOfMsg)
{
    uint bytesWritten = 0;
    TaskCompletionSource<bool> tcs = new TaskCompletionSource<bool>();

    // Use of library "Write" method
    usbStatus = connectedUSBDevice.Write(msg, sizeOfMsg, ref bytesWritten); 

    if(usbStatus != FTDI.FT_STATUS.FT_OK)
    {
       tcs.SetResult(false);
    }
    else
    {
       tcs.SetResult(true);
    }

    return tcs.Task;
}

Затем, чтобы использовать, я просто жду этого:

 isOK = await USBLibrary.SendMessageAsync(data, size);

Эта реализация не завершена? Что мне нужно сделать, чтобы убедиться, что это не блокирует пользовательский интерфейс?

1 Ответ

2 голосов
/ 28 мая 2020

Класс TaskCompletionSource - полезный механизм, когда у вас есть какое-то событие или другой источник асинхронного уведомления, и вы хотите использовать его как Task. По-видимому, в вашем примере это не так. Ваш метод SendMessageAsync практически эквивалентен этому:

public static Task<bool> SendMessageAsync(byte[] msg, int sizeOfMsg)
{
    uint bytesWritten = 0;
    var usbStatus = connectedUSBDevice.Write(msg, sizeOfMsg, ref bytesWritten);
    return Task.FromResult(usbStatus == FTDI.FT_STATUS.FT_OK);
}

Итак, это метод с асинхронным контрактом и синхронной реализацией . Что нехорошо. Намного лучше предоставлять синхронный API и позволять вызывающим абонентам выполнять упаковку самостоятельно (с помощью Task.Run).

public static bool SendMessage(byte[] msg, int sizeOfMsg)
{
    uint bytesWritten = 0;
    var usbStatus = connectedUSBDevice.Write(msg, sizeOfMsg, ref bytesWritten);
    return usbStatus == FTDI.FT_STATUS.FT_OK;
}

Caller:

bool result = await Task.Run(() => SendMessage(msg, sizeOfMsg));
...