Выполнение синхронного кода в async методе

286
21 января 2018, 01:25

Есть у меня promise метод чтения данных из моего потока:

public void ReadAsync<T>(AsyncReadCallback<T> callback, AsyncException exception = null) {
    try {
        Thread thread = new Thread(new ThreadStart(() => {
            try {
                T request = ReadSync<T>();
                callback.Invoke(ref request);
            } catch(Exception e) {
                if(exception == null) {
                    throw e;
                }
                exception.Invoke(e);
            }
        }));
        thread.Start();
    } catch (Exception e) {
        if(exception == null) {
            throw e;
        }
        exception.Invoke(e);
    }
}

Вызывать я его могу, соответственно так:

stream.ReadAsync((string result) => {
    Console.WriteLine("receive: "+result);
},(Exception e) => {
    Console.WriteLine("exception: "+e.Message);
});

Но, в погоне за сахаром, решил попробовать сделать аналогичное поведение через async, что даст возможность выполнять всё без потоков, более того, заставлять делать await в нужных мне местах:

public async Task<T> ReadAsync<T>() {
    return ReadSync<T>();
}

Вызывать планирую, примерно так:

Task<string> taskRead = stream.ReadAsync<string>();
...
string data = await taskRead;
Console.WriteLine(data);

Однако, VS2017 говорит, что мой код будет выполняться только синхронно, и что там нет await. Я не могу понять, возможно ли вообще то что я задумал?

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

Answer 1

Ключевое слово async позволяет выполнять асинхронное ожидание других задач (делать await), но простое добавление этого слова ничего не дает вам в плане многопоточности. Перемещение выполнения в другой поток все еще остается именно вашей задачей.

Выполнить задачу в фоновом потоке можно при помощи Task.Run:

public Task<T> ReadAsync<T>() {
    return Task.Run(() => ReadSync<T>());
}

Однако, написание подобных оберток является дурной практикой: от асинхронного кода ожидается что он по крайней мере часть своей работы выполнит не занимая потока.

Вероятно, правильным будет не делать отдельного метода ReadAsync - а вставить конструкцию Task.Run(() => ReadSync<T>()) непосредственно в том месте где вам нужно прочитать данные:

Task<string> taskRead = Task.Run(() => stream.ReadSync<T>());
...
string data = await taskRead;
Console.WriteLine(data);

А еще правильнее - переписать метод ReadSync так, чтобы он стал асинхронным.

READ ALSO
C# WPF DataGrid Column Context Menu. MVVM

C# WPF DataGrid Column Context Menu. MVVM

Доброго дня всем! Есть задача, нужно вызвать контекстное меню только для столбцов, в котором будет пункт удалить столбецПричем, для Datagrid установлено...

195
Библиотека или Api для распознавания лиц C#

Библиотека или Api для распознавания лиц C#

Есть желание глубже "пощупать" компьютерное зрениеИмеется пару наработок на OpenCV - EMGU с захватом лиц

336
c# - Создание объекта Application

c# - Создание объекта Application

Когда связываю приложения с Excel файлом, сперва создаю объект класса ApplicationПочти всегда сперва using _Excel = Microsoft

298
Настройка виртуальной клавиатуры Android

Настройка виртуальной клавиатуры Android

При вводе текста на мобильном устройстве текст не отображается но он там и есть

275