Асинхронный XmlReader в .NET? - PullRequest
7 голосов
/ 15 февраля 2010

Есть ли способ асинхронного доступа к XmlReader? XML выходит из сети от множества разных клиентов, как в XMPP; это постоянный поток тегов <action>...</action>.

Я хочу использовать интерфейс, подобный BeginRead / EndRead. Лучшее решение, которое мне удалось придумать, - это выполнить асинхронное чтение для 0 байтов в базовом сетевом потоке, а затем, когда поступят некоторые данные, вызвать Read в XmlReader - это, однако, заблокирует, пока все данные из узла становится доступным. Это решение выглядит примерно так

private Stream syncstream;
private NetworkStream ns;
private XmlReader reader;

//this code runs first
public void Init()
{
    syncstream = Stream.Synchronized(ns);
    reader = XmlReader.Create(syncstream);
    byte[] x = new byte[1];
    syncstream.BeginRead(x, 0, 0, new AsynchronousCallback(ReadCallback), null);
}

private void ReadCallback(IAsyncResult ar)
{
    syncstream.EndRead(ar);
    reader.Read(); //this will block for a while, until the entire node is available
    //do soemthing to the xml node
    byte[] x = new byte[1];
    syncstream.BeginRead(x, 0, 0, new AsynchronousCallback(ReadCallback), null);
}

РЕДАКТИРОВАТЬ: Это возможный алгоритм для работы, если строка содержит полный узел XML?

Func<string, bool> nodeChecker = currentBuffer =>
                {
                    //if there is nothing, definetly no tag
                    if (currentBuffer == "") return false;
                    //if we have <![CDATA[ and not ]]>, hold on, else pass it on
                    if (currentBuffer.Contains("<![CDATA[") && !currentBuffer.Contains("]]>")) return false;
                    if (currentBuffer.Contains("<![CDATA[") && currentBuffer.Contains("]]>")) return true;
                    //these tag-related things will also catch <? ?> processing instructions
                    //if there is a < but no >, we still have an open tag
                    if (currentBuffer.Contains("<") && !currentBuffer.Contains(">")) return false;
                //if there is a <...>, we have a complete element.
                //>...< will never happen because we will pass it on to the parser when we get to >
                if (currentBuffer.Contains("<") && currentBuffer.Contains(">")) return true;
                //if there is no < >, we have a complete text node
                if (!currentBuffer.Contains("<") && !currentBuffer.Contains(">")) return true;
                //> and no < will never happen, we will pass it on to the parser when we get to >
                //by default, don't block
                return false;
            };

Ответы [ 6 ]

2 голосов
/ 06 февраля 2013

XmlReader в .NET 4.5 имеет асинхронные версии большинства методов, которые будут включать IO.

Проверьте образец кода здесь .

2 голосов
/ 16 февраля 2010

Буферы XmlReader в блоках по 4 КБ, если я помню, когда смотрел на это пару лет назад. Вы можете добавить свои входящие данные до 4 КБ (ick!) Или использовать лучший анализатор. Я исправил это, перенеся XP (Java) Джеймса Кларка на C # как часть Jabber-Net, здесь:

http://code.google.com/p/jabber-net/source/browse/#svn/trunk/xpnet

Это LGPL, работает только с UTF8, не упакован для использования и почти не имеет документации, поэтому я бы не рекомендовал его использовать. :)

2 голосов
/ 15 февраля 2010

Самое простое, что нужно сделать, это просто поместить его в другой поток, возможно, в ThreadPool, в зависимости от того, как долго он остается активным. (Не используйте потоки пула потоков для действительно длительных задач).

1 голос
/ 12 декабря 2012

Похоже, что DOT NET 4.5 имеет свойство bool Async в XmlReader, которого нет в 3.5. Может быть, это будет работать для вас?

1 голос
/ 15 февраля 2010

Это действительно сложно, потому что XmlReader не предоставляет никакого асинхронного интерфейса.

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

Может быть лучше использовать BeginRead в сетевом потоке для чтения данных, например, фрагментами по 10 КБ (пока система ожидает данные, вы не заблокируете какой-либо поток). Когда вы получите чанк, вы скопируете его в какой-нибудь локальный MemoryStream, а ваш XmlReader будет читать данные из этого MemoryStream.

Это все еще имеет проблему - после копирования 10 КБ данных и вызова Read несколько раз последний вызов заблокируется. Тогда вам, вероятно, потребуется скопировать меньшие порции данных, чтобы разблокировать ожидающий вызов на Read. Как только это будет сделано, вы можете снова начать новый вызов BeginRead для асинхронного чтения большей части данных.

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

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

0 голосов
/ 16 февраля 2010

Вы ищете что-то вроде XamlReader.LoadAsync метода?

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...