Высокий CPU% при использовании делегата - PullRequest
4 голосов
/ 25 января 2012

Я выполняю следующий фрагмент кода, который использует делегат для возврата асинхронного сетевого потока:

static void Main(string[] args)
{
    NetworkStream myNetworkStream;
    Socket socket;
    IPEndPoint maxPort = new IPEndPoint(IPAddress.Parse("xxx.xxx.xxx.xxx"), xxxx);

    socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.IP);
    socket.Connect(maxPort);

    myNetworkStream = new NetworkStream(socket);

    byte[] buffer = new byte[1024];
    int offset = 0;
    int count = 1024;

    string Command = "LOGIN,,,xxxx\n";
    ASCIIEncoding encoder = new ASCIIEncoding();

    myNetworkStream.BeginRead(buffer, offset, count, new AsyncCallback(OnBeginRead), myNetworkStream);
    myNetworkStream.Write(encoder.GetBytes(Command), 0, encoder.GetByteCount(Command));

    while (true) { }
}

public static void OnBeginRead(IAsyncResult ar)
{
    NetworkStream ns = (NetworkStream)ar.AsyncState;
    int bufferSize = 1024;
    byte[] received = new byte[bufferSize];

    ns.EndRead(ar);

    int read;

    while (true)
    {
        if (ns.DataAvailable)
        {
            string result = String.Empty;

            read = ns.Read(received, 0, bufferSize);
            result += Encoding.ASCII.GetString(received);
            received = new byte[bufferSize];

            result = result.Replace(" ", "");
            result = result.Replace("\0", "");
            result = result.Replace("\r\n", ",");

            Console.WriteLine(result);
        }
    }
}

Это работает, но моя загрузка ЦП находится на пределе (50% на Intel Core i3), так что, очевидно, я делаю это неправильно, но как же так?

Спасибо

Ответы [ 3 ]

4 голосов
/ 25 января 2012

Вы читаете только самые первые байты асинхронно, после чего вы попадаете в бесконечный цикл с синхронизированными операциями чтения в вашем методе OnBeginRead (который вводит в заблуждение название BTW).В то же время эти первые байты отбрасываются в вашем текущем коде.

Вам необходимо обработать данные после EndRead (это функция, возвращающая, сколько байтов было считано в буфер в этой асинхронной операции), а затем запустите еще одно асинхронное чтение с помощью BeginRead и вернитесь (в асинхронном коде нет циклов!).

Отредактировано для добавления примера, показывающего, как будет работать асинхронное чтение:

internal class StreamHelper {
    private readonly NetworkStream stream;
    private readonly byte[] buffer = new byte[1024];

    public StreamHelper(Socket socket) {
        stream = new NetworkStream(socket);
    }

    public NetworkStream Stream {
        get {
            return stream;
        }
    }

    public byte[] Buffer {
        get {
            return buffer;
        }
    }
}

private static void Main(string[] args) {
    IPEndPoint maxPort = new IPEndPoint(IPAddress.Parse("xxx.xxx.xxx.xxx"), 100);

    Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.IP);
    socket.Connect(maxPort);

    StreamHelper helper = new StreamHelper(socket);
    helper.Stream.BeginRead(helper.Buffer, 0, helper.Buffer.Length, StreamReadCallback, helper);

    string Command = "LOGIN,,,xxxx\n";
    byte[] bytes = Encoding.ASCII.GetBytes(Command);
    // note: the write isn't async, but should maybe be converted as well
    helper.Stream.Write(bytes, 0, bytes.Length);

    Console.ReadLine(); // wait for a return key press
}

private static void StreamReadCallback(IAsyncResult ar) {
    StreamHelper helper = (StreamHelper)ar.AsyncState;
    // note: EndRead will throw an exception if something went wrong - you should deal with that
    int bytesRead = helper.Stream.EndRead(ar);
    if (bytesRead > 0) {
        string charsRead = Encoding.ASCII.GetString(helper.Buffer, 0, bytesRead);
        Console.Write(charsRead);
        helper.Stream.BeginRead(helper.Buffer, 0, helper.Buffer.Length, StreamReadCallback, helper);
    }
}
1 голос
/ 25 января 2012

Возможно замените неэффективность вращения процессора внизу вашего основного метода с

while (true) { }  

до

Console.ReadLine();

Кстати, Лусеро на месте. Вы переходите в бесконечный цикл (в OnBeginRead) с потоком, который вызывает метод обратного вызова. Это чувствует себя неправильно. Обратные вызовы должны обрабатываться как можно скорее, чтобы позволить вызывающему потоку продолжить обработку. Обычно вы извлекаете данные в обратном вызове и отправляете сигнал в свой собственный поток для обработки остальных. Возможно, здесь поможет ветка TPL .

1 голос
/ 25 января 2012

Вы непрерывно зацикливаетесь на главном потоке:

while (true) { }

Это приводит к тому, что ядро ​​ЦП этого потока постоянно работает на полную мощность.Попробуйте спать, чтобы предотвратить ненужное использование процессором времени процессора:

while (true) { Thread.Sleep(5000); }
...