Vasilievski Ответов: 2

Как я могу назначить собственное продолжение каждой задаче в async/await maneer без того, чтобы одна из них ждала других ?


Привет,

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

Если бы я использовал только задачу Framework 4.0, я бы использовал ContinueWith для каждой задачи.
Но я хочу использовать await / async , и я не нахожу способа сделать что-то после завершения каждой задачи, не ожидая друг друга.

Пример кода для преобразования :

 public async void MainMethod()
        {
            var doSomething = DoSomething();
            var doSomethingElse = DoSomethingElse();
            var doSomethingDifferent = DoSomethingDifferent();

            //These three continuations do not interfer with each other.
            doSomething.ContinueWith(task => ReactToDoSomething(task.Result));
            doSomethingElse.ContinueWith(task => ReactToDoSomethingElse(task.Result));
    doSomethingDifferent.ContinueWith(task=>ReactToDoSomethingDifferent(task.Result));
        }

        public Task<int> DoSomething()
        {
            return Task.FromResult(1);
        }

        public Task<int> DoSomethingElse()
        {
            return Task.FromResult(2);
        }

        public Task<int> DoSomethingDifferent()
        {
            return Task.FromResult(3);
        }
///The code of the react methods must not change.                            
            public void ReactToDoSomething(int i)
        {
            //Update a part of UI 
        }

        public void ReactToDoSomethingElse(int i)
        {
            //Update a part of UI 
        }

        public void ReactToDoSomethingDifferent(int i)
        {
            //Update a part of UI 
        }
    }



Спасибо за вашу помощь.

Что я уже пробовал:

Сначала моя идея состояла в том, чтобы заменить код методов React, но потом пришло, что это ограничение : не меняйте эту часть кода. (Используется где-то еще)

И все же вот что я сделал :

  public class MyClass
    {
        public async void MainMethod()
        {
            var doSomething = DoSomething();
            var doSomethingElse = DoSomethingElse();
            var doSomethingDifferent = DoSomethingDifferent();

            //These three continuations do not interfer with each other.
            ReactToDoSomething(doSomething);
            ReactToDoSomethingElse(doSomethingElse);
            ReactToDoSomethingDifferent(doSomethingDifferent);
        }

        public Task<int> DoSomething()
        {
            return Task.FromResult(1);
        }

        public Task<int> DoSomethingElse()
        {
            return Task.FromResult(2);
        }

        public Task<int> DoSomethingDifferent()
        {
            return Task.FromResult(3);
        }
//Changed React methods but must not, and this is my issue.
        public async Task ReactToDoSomething(Task<int> task)
        {
            //Update a part of UI 
        }

        public async void ReactToDoSomethingElse(Task<int> task)
        {
            //Update a part of UI 
        }

        public async void ReactToDoSomethingDifferent(Task<int> task)
        {
            //Update a part of UI 
        }
    }

2 Ответов

Рейтинг:
2

Richard Deeming

Тебе просто нужно это сделать await задача в методах продолжения.

Вы, вероятно, также захотите подождать, пока все продолжения закончатся в вашем MainMethod.

public async Task ReactToDoSomething(Task<int> task)
{
    int result = await task;
    // Do something here...
}

// Same for other "react to" methods...

public async Task MainMethod()
{
    Task<int> doSomething = DoSomething();
    Task<int> doSomethingElse = DoSomethingElse();
    Task<int> doSomethingDifferent = DoSomethingDifferent();

    Task continuation1 = ReactToDoSomething(doSomething);
    Task continuation2 = ReactToDoSomethingElse(doSomethingElse);
    Task continuation3 = ReactToDoSomethingDifferent(doSomethingDifferent);
    
    await Task.WhenAll(continuation1, continuation2, continuation3);
}



РЕДАКТИРОВАТЬ: Как обсуждалось в комментариях, если вы не хотите изменять исходные методы "ReactTo", вам понадобится метод оболочки для продолжения:
private async Task ReactTo<TResult>(Task<TResult> task, Action<TResult> react)
{
    TResult value = await task;
    react(value);
}

public async Task MainMethod()
{
    Task<int> doSomething = DoSomething();
    Task<int> doSomethingElse = DoSomethingElse();
    Task<int> doSomethingDifferent = DoSomethingDifferent();
    
    Task continuation1 = ReactTo(doSomething, ReactToDoSomething);
    Task continuation2 = ReactTo(doSomethingElse, ReactToDoSomethingElse);
    Task continuation3 = ReactTo(doSomethingDifferent, ReactToDoSomethingDifferent);
    
    await Task.WhenAll(continuation1, continuation2, continuation3);
}


Vasilievski

Здравствуйте, спасибо за ответ.
Как я уже упоминал, цель состоит в том, чтобы не изменять методы начальных продолжений.
Поскольку они не асинхронны, вы не можете ждать от него, верно ?

Richard Deeming

Нет, ты не можешь await от чего-то, чего нет. async.

Если вы хотите оставить оригинал ReactTo... методы, как они есть в настоящее время, вам понадобится другой async метод для продолжения. Например:

private async Task ReactTo<TResult>(Task<TResult> task, Action<TResult> react)
{
    TResult value = await task;
    react(value);
}

public async Task MainMethod()
{
    Task<int> doSomething = DoSomething();
    Task<int> doSomethingElse = DoSomethingElse();
    Task<int> doSomethingDifferent = DoSomethingDifferent();
    
    Task continuation1 = ReactTo(doSomething, ReactToDoSomething);
    Task continuation2 = ReactTo(doSomethingElse, ReactToDoSomethingElse);
    Task continuation3 = ReactTo(doSomethingDifferent, ReactToDoSomethingDifferent);
    
    await Task.WhenAll(continuation1, continuation2, continuation3);
}

Vasilievski

Эта обертка-то, что мне действительно было нужно. Пожалуйста, измените свой ответ так, чтобы я отметил его как принятый. Большое спасибо.

Рейтинг:
1

George Swan

Это может быть хорошей идеей, чтобы реструктурировать ваш class для облегчения использования асинхронного шаблона на основе задач. Я предлагаю вам изменить сигнатуры некоторых ваших методов. Async void методы должны быть зарезервированы для обработчиков событий. Мое предложение было бы примерно таким.


 public async Task MainMethod()
 {
     List<Task> tasks = new List<Task>() {
         DoSomething(),
         DoSomethingElse(),
         DoSomethingDifferent() };

     await Task.WhenAll(tasks);
 }

public async Task DoSomething()
 {
     int result= await Task.FromResult(1);
     if(result>0)
     ReactToDoSomething(result);
 }

 public void ReactToDoSomething(int i)
 {
     //Update a part of UI
 }


Vasilievski

Здравствуйте, спасибо за ответ.
То, что вы делаете, - это плотно сцепленная реакция на что-то происходящее.
Что делать, если я хочу повторно сделать без изменения пользовательского интерфейса (вызова ReactTo... часть) ?

George Swan

Я не уверен, что вы подразумеваете под "без изменения пользовательского интерфейса". Не могли бы вы пояснить, пожалуйста?

Vasilievski

Конечно. Подумайте сделать в качестве индивидуального действия, как вычисление результата. Иногда я хочу, чтобы пользовательский интерфейс отражал это изменение, тогда я вызываю ReactToDoSomething. Тем не менее, бывают моменты, когда я не хочу отражать изменения, тогда я не называю ReactToDoSomething. Ваша идея состоит в том, чтобы объединить оба действия, поэтому у меня нет выбора, отражать или нет изменение.

George Swan

Я изменил метод DoSomething так, что ReactToDoSomething вызывается только в том случае, если результат>0

Vasilievski

Это решение кажется странным.. Ты так не думаешь ?