Перечислимый foreach расширяют - PullRequest
0 голосов
/ 08 ноября 2018

Я создал расширение для Enumerable для быстрого выполнения действия, поэтому я перечислил и в этом методе зацикливаюсь, и если объект, выполняющий метод в определенный период времени, возвращаю, Теперь я хочу сделать вывод универсальным, потому что метод вывода будет отличаться, любые советы о том, что делать

это IE бесчисленное множество процессов, это как балансировка нагрузки, если первый не ответил, второй должен, я хочу вернуть результат ввода Action

public static class EnumerableExtensions
{
    public static void ForEach<T>(this IEnumerable<T> source, Action action, int timeOut)
    {            
        foreach (T element in source)
        {
            lock (source)
            {
                // Loop for all connections and get the fastest responsive proxy 
                foreach (var mxAccessProxy in source)
                {
                    try
                    {
                        // check for the health 
                        Task executionTask = Task.Run(action);
                        if (executionTask.Wait(timeOut))
                        {
                            return  ;
                        }
                    }
                    catch
                    {
                        //ignore 
                    }

                }
            }
        }
    }
}

этот код работает как

  _proxies.ForEach(certainaction, timeOut);

1 Ответ

0 голосов
/ 08 ноября 2018

это повысит производительность и удобочитаемость кода

Нет, определенно не будет :) Более того, вы столкнулись с некоторыми проблемами с этим кодом, такими как избыточная блокировка или проглатывание исключений, но на самом деле не выполняете код параллельно.

Похоже, вы хотите получить максимально быстрый вызов для вашего Action, используя какие-то прокси-объекты. Вам нужно запустить Tasks асинхронно, а не .Wait().

Что-то подобное может быть полезным для вас:

public static class TaskExtensions
{
    public static TReturn ParallelSelectReturnFastest<TPoolObject, TReturn>(this TPoolObject[] pool,
        Func<TPoolObject, CancellationToken, TReturn> func, 
        int? timeout = null)
    {
        var ctx = new CancellationTokenSource();

        // for every object in pool schedule a task
        Task<TReturn>[] tasks = pool
            .Select(poolObject =>
            {
                ctx.Token.ThrowIfCancellationRequested();
                return Task.Factory.StartNew(() => func(poolObject, ctx.Token), ctx.Token);
            })
            .ToArray();

       // not sure if Cast is actually needed, 
       // just to get rid of co-variant array conversion
       int firstCompletedIndex = timeout.HasValue
            ? Task.WaitAny(tasks.Cast<Task>().ToArray(), timeout.Value, ctx.Token)
            : Task.WaitAny(tasks.Cast<Task>().ToArray(), ctx.Token);

        // we need to cancel token to avoid unnecessary work to be done
        ctx.Cancel();

        if (firstCompletedIndex == -1) // no objects in pool managed to complete action in time
            throw new NotImplementedException(); // custom exception goes here

        return tasks[firstCompletedIndex].Result;
    }
}

Теперь вы можете использовать этот метод расширения для вызова определенного действия в любом пуле объектов и получения первого выполненного результата:

var pool = new[] { 1, 2, 3, 4, 5 };
var result = pool.ParallelSelectReturnFastest((x, token) => { 
    Thread.Sleep(x * 200); 
    token.ThrowIfCancellationRequested();
    Console.WriteLine("calculate");
    return x * x; 
}, 100);    
Console.WriteLine(result);    

Выводит:

высчитывает 1

Поскольку первая задача завершит работу за 200 мс, верните ее, а все другие задачи будут отменены с помощью токена отмены.

В вашем случае это будет что-то вроде:

var actionResponse = proxiesList.ParallelSelectReturnFastest((proxy, token) => {
    token.ThrowIfCancellationRequested();
    return proxy.SomeAction();        
});

Некоторые вещи, которые стоит упомянуть:

  • Убедитесь, что ваши действия безопасны. Вы не можете полагаться на то, сколько из них действительно придет к фактическому исполнению вашего действия. Если это действие CreateItem, то вы можете создать множество элементов с помощью различных прокси
  • Это не может гарантировать, что вы будете выполнять все эти действия параллельно, потому что выбор времени выполнения задач зависит от TPL
  • Я реализовал старомодным способом TPL, потому что ваш первоначальный вопрос содержал его. Если возможно, вам нужно переключиться на async / await - в этом случае ваш Func будет возвращать задачи, и вам нужно будет использовать await Task.WhenAny(tasks) вместо Task.WaitAny()
...