Является ли это использование Task.Run() плохой практикой?

#c# #asynchronous #task #background-service

#c# #асинхронный #задача #фоновая служба

Вопрос:

Оправдано ли использование Task.Run в этом случае?
В настоящее время я запускаю этот код в приложении WinForms, но позже он будет использоваться в ASP.NET проект как HostedService / BackgroundService . Я не уверен, сравнимо ли это тогда.

После прочтения нескольких блогов об async / await и Tasks я чувствую Task.Run(() => .. , что это должно быть реализовано в вызывающем методе Manager.SyncLoop() . Но что, если реализация IConnection действительно асинхронна, не будет ли это запахом кода?

 private async void button1_Click(object sender, EventArgs e)
    {
        // this should be handled by the BackgroudService, WinForms is used just for testing
        var m = new Manager();
        m.Connection = new ConnectionA();
        m.ExecuteAsync();
    }
}
public interface IConnection
{
    Task<object> ReadAsync();
}

// assume that i cannot change this 
public class SomeLib
{
    private Random random = new Random();
    public object SyncReading()
    {
        Thread.Sleep(5000);
        return random.Next(); ;
    }
}

public class ConnectionA : IConnection
{
    private SomeLib lib = new SomeLib();
    public Task<object> ReadAsync()
    {
        // is this usage of Task.Run ok?
        var v = Task.Run(() => lib.SyncReading());
        return v;
    }

    // this will block UI
    //public Task<object> ReadAsync()
    //{
    //    return Task.FromResult(lib.SyncReading());
    //}
}

public class Manager 
{
    public IConnection Connection { get; set; }
    public async Task ExecuteAsync()
    {          
        await SyncLoop();
    }

    public async Task SyncLoop()
    {
        while (true)
        {
            var i = await Connection.ReadAsync();

            await Task.Delay(2000);
        }
    }
}
 

Ответ №1:

Во-первых, вы можете изменить IConnection ? Является ли эта синхронная реализация основной или это всего лишь одна из многих?

Если вы можете изменить IConnection , то сделайте это синхронным, и вы можете использовать Task.Run в реализации ExecuteAsync .

Если IConnection необходимо оставаться асинхронным, я бы сказал, реализовать ConnectionA.ReadAsync синхронно. Затем введите Task.Run ExecuteAsync как обычно. Ключом к этому методу является то, что асинхронная ( Task возвращающая) подпись означает, что реализация может быть асинхронной, а не то, что она должна быть асинхронной.

Комментарии:

1. Я контролирую интерфейс IConnection. Но конкретная реализация может быть синхронизирована или асинхронна. Тем не менее, я всегда хочу быть уверенным, что без необходимости не блокирую какой-либо поток. Я думаю, я соглашусь с вашим предложением реализовать Task.Run в методе ExecuteAsync, так как я все равно должен это сделать (см. Вашу ошибку BackgroundService ;))

2. ОК. Тогда я бы оставил IConnection.ReadAsync подпись асинхронной и просто разрешил как асинхронные, так и синхронные реализации.

3. Под «сохраняйте последовательность как однонаправленную» вы имеете в виду, что я должен сохранить асинхронное именование и обеспечить асинхронное выполнение при Task.Run вызове ExecuteAsync ? Просто для моего пояснения.

4. Да; под «асинхронной сигнатурой» я подразумеваю Async суффикс и Task (или подобный задаче) возвращаемый тип.