Erzwingen Sie, dass WCF einen Thread verwendet

Ich habe eine Konsolenanwendung, die eine externe Bibliothek verwendet. Die Bibliothekbesteht darauf auf immer aus dem gleichen Thread aufgerufen werden; es sperrt sonst. (Ich habe versucht, als STA zu laufen, um zu sehen, ob dies das Problem beheben würde - aber nein, es ist wirklich sobesteht darauf Sie müssen immer den gleichen Thread verwenden. Meine Vermutung ist Thread-lokaler Speicher ...)

Bisher kommunizierte die Anwendung über eine rohe TCP-Verbindung. Ich habe es jedoch kürzlich geändert, um WCF zu verwenden. Nun scheint es, dass WCF willkürlich Threads auswählt, um meinen Code auszuführen, was zu spektakulären Fehlern führt.

Ich muss unbedingt 100%verhindern Dieses Verhalten passiert nie. Ist mir egalwelche Thread mein Code läuft in, solange es immer istdas Gleiche Faden! Ich habe die letzten Tage damit verbracht, das Gesicht des Internets zu durchsuchen und wiederholt meinen Kopf in die Tastatur zu schlagen, um WCF dazu zu bringen, keine Threads mehr zu verwenden.

Dinge, die ich versucht habe:

InstanceContextMode.Single erzwingt die Verwendung einer einzelnen WCFObjekt für meine Sachen, die nützlich sind, aber das Problem nicht direkt ansprechen.

ConcurrencyMode = ConcurrencyMode.Single garantiert, dass nur ein Thread gleichzeitig ausgeführt wird, verspricht es aber nichtwelcher.

UseSynchronizationContext scheint auf nichts eine Auswirkung zu haben, so gut ich das beurteilen kann.

Mit diesen Flags habe ich es geschafft, an den Punkt zu kommen, an dem jederKlient bekommt einen einzigen Thread. Das bedeutet aber immer noch, dass wenn der erste Client die Verbindung trennt und der nächste Client die Verbindung herstellt, ich einen anderen Thread erhalte und die Bibliothek mein Programm hängt.

Ich habe auch den Brute-Force-Ansatz ausprobiert: Ich habe eine Klasse geschrieben, die einen eigenen Arbeitsthread erstellt und es Ihnen ermöglicht, Code für die Ausführung in diesem Thread in die Warteschlange zu stellen. Ich habe die Klasse isoliert getestet, und es scheint perfekt zu funktionieren, aber wenn ich versuche, sie in meiner WCF-Anwendung zu verwenden, passiert etwas äußerst Seltsames. Das Programm verarbeitet diezuerst Befehl perfekt, gibt ein Ergebnis an den Client zurück und bleibt dann für immer hängen.

Dieses Verhalten macht absolut keinen Sinn. ich kannsehen Aus der Konsolenausgabe geht hervor, dass es nicht in der externen Bibliothek und auch nicht in meiner neuen Arbeitswarteschlangen-Klasse steckt. Also, wo zum Teufel ist es stecken geblieben ?!

An diesem Punkt würde ich normalerweise mehr Debug-Drucke einfügen - außer Sie können keine Debug-Drucke in WCF einfügen, nur den Code, den es aufruft. Ich kann also nicht sagen, was der Service-Host versucht ...

Ich habe verschiedene SO-Antworten zu diesem Thema gesehen, die alle besagen, dass die Lösung etwas völlig anderes ist. Es gibt einen, der von "Synchronisationskontexten" spricht und mehr oder weniger unverständlich ist - aber es scheint, als würde er das Gleiche tun wie meine Arbeitswarteschlangen-Klasse. Es gibt noch eine andere Möglichkeit, verschiedene Service-Flags zu setzen - was ich bereits getan habe und es nicht behoben hat. Jemand anderes schlug vor, Ihre eigenen zu implementierenIOperationBehaviour (Das sieht wahnsinnig kompliziert aus).

Im Grunde bin ich mir zum jetzigen Zeitpunkt nicht sicher, was zur Hölle zu tun ist, und ich kann dieses Zeug nicht zum Laufen bringen. PLZ-Hilfe. :-(

[Konsolenanwendung, selbst gehostet,NetTcpBinding, Konfiguration in Code, .NET 4 - falls es darauf ankommt ...]

Hier ist die Warteschlangenklasse, falls es darauf ankommt: [Es ist ein bisschengroßÜbrigens]

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.
        }
    }
}

Wie gesagt, wenn ich dies isoliert teste, scheint es gut zu funktionieren. [Murmeln Sie etwas über Multithread-Codierung und Determinismus.] Wenn Sie in WCF ausgeführt werden, schlägt dies immer genau an der gleichen Stelle fehl.

Antworten auf die Frage(3)

Ihre Antwort auf die Frage