Версия для печати
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум на Исходниках.RU > .NET: Распределенные приложения > Одно и то же работает в Remoting и не работает в WCF


Автор: hd44780 13.04.12, 10:35
Есть класс, реализующий функционал Remoting/WCF, в нём метод:

<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
            /// <summary>
            /// Подать команду серверу
            /// </summary>
            /// <param name="sCommand">Сериализованый объект команды</param>
            /// <returns>Сериализованный ответ</returns>
            public string SetCommand( string sCommand )
            {
                // поместить команду в очередь ядра
                MainFormServer.queueFromRemoting.Enqueue ( sCommand );
     
    //            File.AppendAllText("log.txt", "Command: " + sCommand + "\n enqueueed\n",
    //                Encoding.GetEncoding(1251));
     
                // Ждать завершения
                while ( MainFormServer.queueToRemoting.Count == 0 );
     
                // вернуть клиенту ответ
                return MainFormServer.queueToRemoting.Dequeue ( );
            }


Что делает, думаю, понятно...
В форме работает таймер, который мониторит queueFromRemoting (она public static) и отрабатывает команды.

Это проработало в Remoting уже лет 5, всё как часы, идеально. И не на одной машине.
Когда я переключился на WCF, метод вызывается (следил по логу - закомментаренная строчка), команда вроде проходит, но в очереди ничего не появляется (делал лог в таймере, длина очереди всегда ==0).
При этом сервер виснет, жрёт проц по-полной (50% 2-х голового камня) :crazy: , а клиент отваливается где-то через минуту по таймауту :wacko: .
Сервер, естественно, приходится вышибать руками через диспетчер задач.

Исключений нигде не вылетает.
В чём м.б. причина? Или к статическому объекту нельзя из WCF обращаться?

Спасибо.

Автор: hd44780 03.08.12, 08:52
Мда ...

Разобрался сам ... Спустя много дней :rolleyes: .

Автор: Mr.Delphist 14.08.12, 09:35
Поделись?

Автор: hd44780 14.08.12, 10:49
Делюсь :) .

Зависон сервера и дикая загрузка проца связана были со строчкой

// Ждать завершения
while ( MainFormServer.queueToRemoting.Count == 0 );

Таймер формы не видит команду и ничего не делает, в выходной очереди queueToRemoting ничего не появляется, соответственно цикл висит и вешает всё. В такие циклы надо что-то типа Thread.Sleep вставлять, либо таймаут делать, либо вообще менять идеологию опроса очереди.
Но это так, мелкие сопутствующие проблемы ... Хоть и неправильно было изначально так делать. Но что сделано, то сделано ....

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

<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
      
            /// <summary>
            /// Подать команду серверу
            /// </summary>
            /// <param name="sCommand">Сериализованый объект команды</param>
            /// <returns>Сериализованный ответ</returns>
            public string SetCommand( string sCommand )
            {
                // Проверка объекта команды
                if ( !CllCntCommand.CanDeserializeDoc ( sCommand ) )
                { // ошибка
                    CllCntServerAnswer answ = new CllCntServerAnswer ( -1,  "ОШИБКА СЕРВЕРА: Неверная команда" );
                    answ.text = sCommand;
     
                    return answ.SerializeStr ( );
                } // if
     
                return (string)MainFormServer.mForm.Invoke(MainFormServer.mForm.commandDlg, sCommand);
            } // SetCommand


Куски из MainFormServer:

В переменных формы
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
            /// <summary>
            /// Ссылка на форму
            /// </summary>
            public static MainFormServer mForm;
     
            /// <summary>
            /// Тип делегата
            /// </summary>
            /// <param name="myString">Команда</param>
            /// <returns>ОТвет на команду</returns>
            public delegate string DoCommandDelegate(String myString);
     
            /// <summary>
            /// Делегат для подачи команды
            /// </summary>
            public DoCommandDelegate commandDlg;


Это статическая ссылка на форму и делегат, обрабатывающий команды клиента.
В событии загрузки формы инициализируем добро:
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
                // Делегат выполнения команд
                mForm = this;
                commandDlg = DoCommand;

Может можно и в конструкторе, не знаю, не проверял, в загрузке мне кажется надёжнее... Типа объект формы уже полностью инициализирован и пр.

Метод обрабатывающий команды:
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
            private string DoCommand(string sCommand)
            {
                return "Ответ на команду "+sCommand;
            } // DoCommand


Таймер и очереди я вообще убрал. Всё равно у меня 100% синхронная отработка команд.

Так делать вообще правильнее, хотя я и по сей день не понимаю - почему старый вариант с таймером и очередями как часы работал в Remoting без единого сбоя, а в WCF вообще не работает :-? .
Если кто мне объяснит сей "парадокс", буду благодарен.

И почему в книгах про WCF даются только примитивные примеры для детского садика типа a+b/c, а действительно сложных вещей с подводными камнями (типа описанных выше) никто нигде не описывает?
Нужели все думают, что 100% задач решаемы исключительно в рамках этого одного класса :wacko: примитивными методами вроде a+b/c?

Автор: maxim84_ 14.08.12, 15:04
Цитата
while ( MainFormServer.queueToRemoting.Count == 0 );

сама по-себе конструкция плохая. Будет много таких потоков, то ресурсов от процессора не останется. лучше использовать [Auto/Manual]ResetEvent.
Или колбеки использовать, что лучше всего, в WCF есть такая возможность.
Или уж тогда:

<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    while ( MainFormServer.queueToRemoting.Count == 0 )
    {
      Thread.Sleep(1);
    }


Но ручные слипы потоков - это очень плохо. Старайтесь избегать таких конструкций.

Что касается вашего вопроса, то сложно сказать, есть предположение, что каждое обращение к переменной создавался пакет на хост ремотящего объекта MainFormServer, что выглядело примерно так:

<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    int count = MainFormServer.RequestQueueCount();
    while ( count == 0 )
    {
       count = MainFormServer.RequestQueueCount();
    }

Поскольку RequestQueueCount вызов, то поток не жрал ресурсы, а честно выполнял инструкции и стоял на объектах синхронизации внутри ремотинга.

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

Надо будет поиграться :)

Автор: hd44780 08.07.14, 09:39
Спасибо, конечно, но уже всё это увы давно неактуально. Жизнь и судьба увели меня да-алеко от C# и .NET.
А в виде хобби этим заниматься совершенно некогда.

PS.
Если честно, я даже не знаю, что такое SCRUM :yes-sad: ...

Powered by Invision Power Board (https://www.invisionboard.com)
© Invision Power Services (https://www.invisionpower.com)