C #, максимизировать параллелизм потока - PullRequest
0 голосов
/ 28 июня 2018

С помощью Google и сообщества я смог создать хороший набор методов, позволяющих мне асинхронно вызывать функцию. Эта функция тестирует свойства удаленного хоста, поэтому большую часть времени она простаивает. По этой причине я хотел бы максимизировать количество одновременных потоков, запускаемых таким образом, чтобы все вызовы могли быть обработаны за минимальное время.

Вот код, который у меня есть:

// Check remote host connectivity
public static class CheckRemoteHost
{

    // Private Class members
    private static bool AllDone     = false;
    private static object lockObj   = new object();
    private static List<string> IPs;


    // Wrapper: manage async method <Ping>
    public static List<string> Ping(HashSet<string> IP_Ports, int TimeoutInMS = 100)
    {// async worker method:  check remote host via <Ping>

        // Locals
        IPs = new List<string>();

        // Perform remote host check
        AllDone = false;
        Ping_check(IP_Ports, TimeoutInMS);
        while (!AllDone) { CommonLib.Utils.ApplicationWait(10, 10); }

        // Finish
        return IPs;
    }
    private static async void Ping_check(HashSet<string> IP_Ports, int timeout)
    {
        // Locals
        var tasks = new List<Task>();

        // Build task-set for parallel Ping checks
        foreach (string host in IP_Ports)
        {
            var task = PingAndUpdateAsync(host, timeout);
            tasks.Add(task);
        }

        // Start execution queue
        await Task.WhenAll(tasks).ContinueWith(t =>
        {
            AllDone = true;
        });

    }
    private static async Task PingAndUpdateAsync(string ip, int timeout)
    {

        // Locals
        System.Net.NetworkInformation.Ping ping;
        System.Net.NetworkInformation.PingReply reply;

        try
        {
            ping    = new System.Net.NetworkInformation.Ping();
            reply   = await ping.SendPingAsync(ip, timeout);

            if(reply.Status == System.Net.NetworkInformation.IPStatus.Success)
            {
                lock (lockObj)
                {
                    IPs.Add(ip);
                }
            }
        }
        catch
        {
            // do nothing
        }
    }

}// end     public static class CheckRemoteHost

Этот код протестирован довольно обширно, и код кажется стабильным и достоверно сообщает о живых хостах. Сказав это, я знаю, что он порождает только 8 потоков одновременно (= количество логических ядер на моей тестовой машине).

Ключевая часть кода выглядит так:

// Start execution queue
await Task.WhenAll(tasks).ContinueWith(t =>
{
    AllDone = true;
});

Здесь я хотел бы увеличить / максимизировать количество одновременно запущенных потоков до примерно 25 на ядро ​​(помните, что работа потока на 99% простаивает).

Пока что мое исследование параллелизма потоков выявило явный поток и подходы Parallel.For. Тем не менее, они, похоже, имеют тот же недостаток порождения не более 8 потоков.

Любая помощь будет принята с благодарностью, поэтому большое спасибо всем за внимание!

1 Ответ

0 голосов
/ 30 июня 2018

Ты усложняешь свою жизнь с помощью имеющегося у тебя кода. В нем много сантехники, которая не нужна, и вы делитесь статическими полями, что приведет к сбою кода, если вы вызовете Ping во второй раз во время работы первого.

Тебе нужно избавиться от всего этого.

Я бы предложил использовать Reactive Framework от Microsoft - просто NuGet «System.Reactive» и добавьте using System.Reactive.Linq; к своему коду. Тогда вы можете сделать это:

public static class CheckRemoteHost
{
    public static IList<string> Ping(HashSet<string> IP_Ports, int TimeoutInMS = 100)
    {
        var query =
            from host in IP_Ports.ToObservable()
            from status in Observable.FromAsync(() => PingAsync(host, TimeoutInMS))
            where status
            select host;

        return query.ToList().Wait();
    }

    private static async Task<bool> PingAsync(string ip, int timeout)
    {
        try
        {
            var ping = new System.Net.NetworkInformation.Ping();
            var reply = await ping.SendPingAsync(ip, timeout);

            return reply.Status == System.Net.NetworkInformation.IPStatus.Success;
        }
        catch
        {
            return false;
        }
    }
}

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

...