Заставить WCF использовать один поток
У меня есть консольное приложение, которое использует внешнюю библиотеку. Библиотеканастаивает на том, всегда вызываться из одного и того же потока; иначе он запирает. (Я попытался запустить как STA, чтобы увидеть, исправит ли это, но нет, это действительнонастаивает на том, Вы должны всегда использовать одну и ту же нить. Я думаю, что поток локального хранилища ...)
Ранее приложение связывалось с использованием необработанного TCP-соединения. Тем не менее, я недавно изменил его, чтобы использовать WCF. Теперь кажется, что WCF случайным образом выбирает потоки для запуска моего кода, что приводит к впечатляющей ошибке.
Мне нужно абсолютно 100%предотвращать это поведение никогда не происходит. Мне все равнокоторый поток мой код работает, пока он всегдатот же самый нить! Я провел последние несколько дней, обыскивая лицо Интернета и неоднократно ломая голову в клавиатуру в отчаянной попытке заставить WCF прекратить использовать потоки.
Вещи, которые я пробовал:
InstanceContextMode.Single
заставляет WCF использовать одинобъект для моих вещей, которые полезны, но не решают проблему напрямую.
ConcurrencyMode = ConcurrencyMode.Single
гарантирует, что одновременно будет работать только один поток, но не обещаеткоторый из.
UseSynchronizationContext
Кажется, что ни на что не влияет, насколько я могу судить.
Используя эти флаги, мне удалось добраться до точки, где каждыйклиент получает одну нить. Но это все еще означает, что, когда первый клиент отключается, а следующий клиент подключается, я получаю другой поток, и библиотека зависает моей программы.
Я также попробовал подход грубой силы: я написал класс, который создает собственный рабочий поток и позволяет ставить код в очередь для выполнения в этом потоке. Я тестировал класс изолированно, и он, кажется, отлично работает, но когда я пытаюсь использовать его в своем приложении WCF, происходит что-то чрезвычайно странное. Программа обрабатываетпервый Команда отлично, возвращает результат клиенту, а затем зависает навсегда.
Такое поведение не имеет абсолютно никакого смысла. Я могувидеть из вывода консоли, что он не застрял во внешней библиотеке, и он не застрял в моем новом классе организации очередей. Так где, черт возьми, он застрял ?!
На этом этапе я обычно начинаю вставлять больше отладочных отпечатков - за исключением того, что вы не можете вставлять отладочные отпечатки в WCF, только код, который он вызывает. Поэтому я не могу сказать, что пытается сделать хост службы ...
Я видел разные ответы SO на эту тему, все из которых говорят, что решение - это нечто совершенно другое. Есть один, который говорит о «контекстах синхронизации» и является более или менее непонятным - но, похоже, он будет делать то же самое, что и мой класс рабочей очереди. Есть еще один способ установки различных служебных флагов - что я уже сделал, и это не помогло. Кто-то еще предложил реализовать свой собственныйIOperationBehaviour
(который выглядит безумно сложным).
По сути, на данный момент я не уверен, что, черт возьми, делать, и я не могу заставить этот материал работать. Пожалуйста, помогите. :-(
[Консольное приложение, самостоятельно размещенное,NetTcpBinding
, конфигурация в коде .NET 4 - в случае, если это имеет значение ...]
Вот класс рабочей очереди, если он имеет значение: [Это немногобольшойКСТАТИ.]
public sealed class ThreadManager
{
private Thread _thread; // Worker thread.
private volatile Action _action; // Enqueued method.
private volatile object _result; // Method result.
private volatile bool _done; // Has the method finished executing?
public void Start()
{
_action = null;
_result = null;
_done = true;
_thread = new Thread(MainLoop);
_thread.Start();
}
public void ExecuteInWorkerThread(Action action)
{
// Wait for queue to empty...
Monitor.Enter(this); // Lock the object, so we can inspect it.
while (_action != null)
{
Monitor.Pulse(this); // Wake up the next thread waiting on the lock.
Monitor.Wait(this); // Release lock, wait for Pulse(), acquire lock.
}
// Enqueue action...
_action = action;
_done = false;
// Wait for action to complete...
while (! _done)
{
Monitor.Pulse(this); // Wake up the next thread waiting on the lock.
Monitor.Wait(this); // Release lock, wait for Pulse(), acquire lock.
}
// Worker thread has finished doing it's thing now.
Monitor.Pulse(this); // Wake up any threads trying to enqueue work.
Monitor.Exit(this); // Release the lock.
}
public T ExecuteInWorkerThread<T>(Func<T> action)
{
ExecuteInWorkerThread(() => { _result = action(); });
return (T) _result; // If this cast fails, something has gone spectacularly wrong!
}
// Runs forever in worker thread.
private void MainLoop()
{
while (true)
{
// Wait for an action to dequeue...
Monitor.Enter(this); // Lock object so we can inspect it.
while (_action == null)
{
Monitor.Pulse(this); // Wake up the next thread waiting on the lock.
Monitor.Wait(this); // Release lock, wait for Pulse(), acquire lock.
}
// Dequeue action...
var action = _action;
_action = null;
// Perform the action...
action(); // Do the actual action!
_done = true; // Tell the caller we're done.
Monitor.Pulse(this); // Wake the caller up.
Monitor.Exit(this); // Release the lock.
}
}
}
Как я уже сказал, когда я тестирую это изолированно, кажется, что оно работает нормально. [Что-то бормочет о многопоточном кодировании и детерминизме.] При запуске в WCF всегда происходит сбой в одной и той же точке.