c # Fire Async от Loop - PullRequest
       9

c # Fire Async от Loop

1 голос
/ 01 октября 2019

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

async task Start()
{

    List<Task<Tuple<string, int>>> AsyncTasks = new List<Task<Tuple<string, int>>>();

    foreach (string name in NameArray)
    {
        //fire all the async task in this loop
        AsyncTasks.Add(DoAsyncTask(name));
    }

    //Method 1 I tried
    Task<Tuple<string, int>[]> Tasks = Task.WhenAll(VirtualOutPortTasks);

    foreach (var tupleStringInt in Tasks.Result)
    {
        //handle result either async or nonasync here
        //each async task can be handled individually, I dont need to wait for all of them.
        HandleResult(tupleStringInt);
    }

    // Method 2 I tried
     foreach (var tupleStringInt in await Tasks)
    {
        //handle result either async or nonasync here
        //each async task can be handled individually, I dont need to wait for all of them.
        HandleResult(tupleStringInt);
    }
}



async Task<Tuple<string,int>> DoAsyncTask(string name)
    {
        // the someOtherAsyncTask runs an Async task in an DLL that I cant see, 
        // it returns an Task<int>
        int resultInt = await SomeOtherAsyncTask(name);
        return Tuple.Create(name, resultInt );
    }

У меня есть 6 значений в NameArray. Каждая операция занимает около 5 секунд. Прямо сейчас этот код будет зависать на 30 секунд, пока он работает. Так что, должно быть, делаешь что-то правильно.

Ответы [ 2 ]

1 голос
/ 01 октября 2019

Есть много способов сделать это, это только некоторые примеры.

Предполагается, что вы хотите запустить группу задач и pipe их в метод с именем handle (асинхронный или нет), чтобы они работали асинхронно и не ожидалидля всего остального, чтобы закончить.

Примечание оба решения ждут завершения, вы можете сделать это или нет

private static async Task<string> DoAsyncTask(string name)
{
   Console.WriteLine($"Do {name}");
   // simulate a delay
   await Task.Delay(5000);
   return name;
}

private static async Task HandleAsync(Task<string> task)
{
   var name = await task;
   Console.WriteLine($"Done {name}");
}

public static async Task Main()
{
   // List of names
   var nameArray = new[] { "task 1", "task 2", "task 3", "task 4", "task 5", "task 6", "task 7", };

   Console.WriteLine("Started " + DateTime.Now);

   // pump the names in to do async, then the resulting tasks in toHandleAsync
   var taskList = nameArray.Select(DoAsyncTask)
                           .Select(HandleAsync);

   // wait for all tasks 
   await Task.WhenAll(taskList);

   Console.WriteLine("Finished " + DateTime.Now);
   Console.ReadKey();

}

Вывод

Started 1/10/2019 12:08:03 PM
Do task 1
Do task 2
Do task 3
Do task 4
Do task 5
Do task 6
Do task 7
Done task 5
Done task 7
Done task 6
Done task 4
Done task 2
Done task 1
Done task 3
Finished 1/10/2019 12:08:08 PM

Решение потока данных вам потребуется System.Threading.Tasks.Dataflow nuget

private static async Task<(string, DateTime)> DoAsyncTask(string name)
{
   Console.WriteLine($"Do {name}");
   // simulate a delay
   await Task.Delay(5000);

   return (name, DateTime.Now);
}

private static void Handle((string, DateTime) someTuple)
{
   Console.WriteLine($"Name {someTuple.Item1}, value {someTuple.Item2}");
}

public static async Task Main()
{
   // List of names
   var nameArray = new[] { "task 1", "task 2", "task 3", "task 4", "task 5", "task 6", "task 7", };

   var options = new ExecutionDataflowBlockOptions()
                 {
                    EnsureOrdered = false,
                    MaxDegreeOfParallelism = 50
                 };
   var transform = new TransformBlock<string, (string, DateTime)>(DoAsyncTask, options);
   var actionBlock = new ActionBlock<(string, DateTime)>(Handle, options);

   transform.LinkTo(actionBlock, new DataflowLinkOptions(){PropagateCompletion = true});

   Console.WriteLine("Started " + DateTime.Now);

   foreach (var name in nameArray)
   {
      transform.Post(name);
   }

   transform.Complete();

   await actionBlock.Completion;

   Console.WriteLine("Finished " + DateTime.Now);
   Console.ReadKey();

}

Вывод

Started 1/10/2019 12:21:17 PM
Do task 2
Do task 1
Do task 4
Do task 3
Do task 5
Do task 6
Do task 7
Name task 2, value 1/10/2019 12:21:22 PM
Name task 5, value 1/10/2019 12:21:22 PM
Name task 7, value 1/10/2019 12:21:22 PM
Name task 3, value 1/10/2019 12:21:22 PM
Name task 6, value 1/10/2019 12:21:22 PM
Name task 4, value 1/10/2019 12:21:22 PM
Name task 1, value 1/10/2019 12:21:22 PM
Finished 1/10/2019 12:21:22 PM

Обратите внимание, что вы можете сделать это и с RX

0 голосов
/ 01 октября 2019

OP здесь, благодаря примеру кода @ TheGeneral. Вот что сработало для меня.

async Task Start()
{
    //Method 1: fire all async event, then wait for all async task are completed
    //before handling all the results non asyncally

    List<Task<int>> FiredTasks = new List<Task<int>>();
    for (int i = 0; i< 6; i++)
    { 
        Debug.Log("async task firing "+i);
        Tasks.Add(DoAsyncTask("someStringValue", i));
    }

    foreach (int item in await Task.WhenAll(FiredTasks))
    {
        Debug.Log("async task ended, returned result: " + item);
        //add code to handle the returned result here.
    }

    //Method 2, fire all async event, then handle each result as they complete without waiting for others
    List<Task<int>> FiredTasks = new List<Task<int>>();
    for (int i = 0; i < 6; i++)
    {
        Debug.Log("Firing async event "+i);
        Tasks.Add(DoAsyncTask("someStringValue", i));
    }

    //If I understnd correctly, this loop basically tells each of the fired async task what to do when they are done.
    foreach (Task<int> task in FiredTasks)
    {
        await HandleAsyncResult(task);
    }
}

async Task<int> DoAsyncTask(string name, int index)
{
    await Task.Delay(6000);
    // or you can do add some randome delay in here so you can see the individual task completing at different time
    await Task.Delay(UnityEngine.Random.Range(1,10) * 1000);
    return index;
}

async Task HandleAsyncResult(Task<int> task)
{
    int taskResult = await task;
    Debug.Log("Done " + taskResult);
}

Debug.Log () является эквивалентом Unity для Console.WriteLine ();

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

...