强制WCF使用一个线程 [英] Force WCF to use one thread

查看:69
本文介绍了强制WCF使用一个线程的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个使用外部库的控制台应用程序.总是在同一线程中调用库 insists ;否则会锁定. (我确实尝试过以STA的身份运行,看看是否可以解决该问题-但是,确实,它必须 insist 始终使用同一线程.我的猜测是线程本地存储...)

以前,应用程序是使用原始TCP连接进行通信的.但是,我最近将其更改为使用WCF.现在看来WCF随机选择线程来运行我的代码,这会导致严重的失败.

我绝对必须100%防止这种行为发生.我不在乎我的代码在其中运行的 线程,只要它总是相同的线程即可!最近几天,我一直在搜寻Internet的面孔,并反复地将头砸向键盘,以期使WCF停止使用线程.

我尝试过的事情:

  • InstanceContextMode.Single强制WCF对我的东西使用单个 object ,这很有用,但不能直接解决问题.

  • ConcurrencyMode = ConcurrencyMode.Single保证只有一个线程可以同时运行,但不保证哪个线程.

  • 据我所知,
  • UseSynchronizationContext似乎对任何东西都没有影响.

使用这些标志,我设法达到了每个 client 获得单个线程的地步.但这仍然意味着当第一个客户端断开连接而下一个客户端连接时,我得到了另一个线程,并且库挂起了我的程序.


我还尝试了蛮力方法:我编写了一个类,该类创建了自己的工作线程,并允许您将代码排队在该线程上执行.我单独测试了该类,它看起来运行良好,但是当我尝试在WCF应用程序中使用它时,会发生一些非常奇怪的事情.该程序完美地处理了 first 命令,将结果返回给客户端,然后永久挂起.

这种行为绝对没有任何意义.我可以从控制台输出看到,它没有卡在外部库中,也没有卡在新的工作队列类中.那么,它到底在哪里卡住了?!

在这一点上,我通常会开始插入更多的调试打印-除非您不能将调试打印插入到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中运行时,它总是在完全相同的位置失败.

解决方案

在进一步检查中,似乎我忘了包装一些外部库调用,导致库死锁了服务器.现在,我已经解决了所有问题,一切正常……

对不起,你很愚蠢.

I have a console application which uses an external library. The library insists on always being called from the same thread; it locks up otherwise. (I did try running as STA to see if that would fix it - but no, it really insists you have to always use the same thread. My guess is thread-local storage...)

Previously the application communicated using a raw TCP connection. However, I recently changed it to use WCF. Now it seems that WCF chooses threads at random to run my code, which results in spectacular failure.

I need to absolutely 100% prevent this behaviour from ever happening. I don't care which thread my code runs in, so long as it is always the same thread! I've spent the last few days scouring the face of the Internet and repeatedly smashing my head into the keyboard in a desperate attempt to make WCF stop using threads.

Things I've tried:

  • InstanceContextMode.Single forces WCF to use a single object for my stuff, which is useful, but doesn't directly address the problem.

  • ConcurrencyMode = ConcurrencyMode.Single guarantees that only one thread will be running concurrently, but doesn't promise which one.

  • UseSynchronizationContext seems to not have any effect on anything, as best as I can tell.

Using these flags, I managed to get to the point where each client gets a single thread. But that still means that when the first client disconnects and the next client connects, I get a different thread and the library hangs my program.


I also tried the brute-force approach: I wrote a class that creates its own worker thread and lets you enqueue code to execute on that thread. I tested the class in isolation, and it appears to work perfectly, but when I try to use it in my WCF application, something extremely strange happens. The program processes the first command perfectly, returns a result to the client, and then hangs forever.

This behaviour makes absolutely no sense whatsoever. I can see from the console output that it isn't stuck in the external library, and it isn't stuck in my new work-queueing class either. So where the hell has it got stuck?!

At this point, I would normally start inserting more debug prints - except you can't insert debug prints into WCF, only the code that it calls. So I cannot tell what the service host is trying to do...


I've seen various SO answers on this topic, all of which say that the solution is something completely different. There's one that talks about "synchronisation contexts" and is more or less incomprehensible - but it appears it would do the same thing as my work queue class. There's another about setting various service flags - which I've already done and it didn't fix it. Somebody else suggested implementing your own IOperationBehaviour (which looks insanely complicated).

Basically at this point I'm not sure what the hell to do, and I can't get this stuff to work. Plz help. :-(

[Console application, self-hosted, NetTcpBinding, configuration in code, .NET 4 - in case it matters...]


Here is the work-queue class, in case it matters: [It's a tad large, BTW.]

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

As I said, when I test this in isolation, it appears to work just fine. [Mutter something about multithreaded coding and determinism.] When run in WCF, it always fails at exactly the same point.

解决方案

On further examination, it seems that I forget to wrap some of the external library calls, causing the library to deadlock the server. Now that I've fixed that, everything works perfectly...

Sorry for being dumb.

这篇关于强制WCF使用一个线程的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆