开发者

How to force multiple commands to execute in same threading timeslice?

开发者 https://www.devze.com 2022-12-12 03:37 出处:网络
I have a C# app that needs to do a hot swap of a data input stream to a new handler class without breaking the data stream.

I have a C# app that needs to do a hot swap of a data input stream to a new handler class without breaking the data stream.

To do this, I have to perform multiple steps in a single thread without any other threads (most of all the data recieving thread) to run in between them due to CPU switching.

This is a simplified version of开发者_如何转开发 the situation but it should illustrate the problem.

void SwapInputHandler(Foo oldHandler, Foo newHandler)
{
    UnhookProtocol(oldHandler);
    HookProtocol(newHandler);
}

These two lines (unhook and hook) must execute in the same cpu slice to prevent any packets from getting through in case another thread executes in between them.

How can I make sure that these two commands run squentially using C# threading methods?

edit

There seems to be some confusion so I will try to be more specific. I didn't mean concurrently as in executing at the same time, just in the same cpu time slice so that no thread executes before these two complete. A lock is not what I'm looking for because that will only prevent THIS CODE from being executed again before the two commands run. I need to prevent ANY THREAD from running before these commands are done. Also, again I say this is a simplified version of my problem so don't try to solve my example, please answer the question.


Performing the operation in a single time slice will not help at all - the operation could just execute on another core or processor in parallel and access the stream while you perform the swap. You will have to use locking to prevent everybody from accessing the stream while it is in an inconsistent state.


Your data receiving thread needs to lock around accessing the handler pointer and you need to lock around changing the handler pointer.

Alternatively if your handler is a single variable you could use Interlocked.Exchange() to swap the value atomically.


Why not go at this from another direction, and let the thread in question handle the swap. Presumably, something wakes up when there's data to be handled, and passes it off to the current Foo. Could you post a notification to that thread that it needs to swap in a new handler the next time it wakes up? That would be much less fraught, I'd think.


Okay - to answer your specific question.

You can enumerate through all the threads in your process and call Thread.Suspend() on each one (except the active one), make the change and then call Thread.Resume().


Assuming your handlers are thread safe, my recommendation is to write a public wrapper over your handlers that does all the locking it needs using a private lock so you can safely change the handlers behind the scenes.

If you do this you can also use a ReaderWriterLockSlim, for accessing the wrapped handlers which allows concurrent read access.

Or you could architect your wrapper class and handler clases in such a way that no locking is required and the handler swamping can be done using a simple interlocked write or compare exchange.

Here's and example:

public interface IHandler
{
    void Foo();
    void Bar();
}

public class ThreadSafeHandler : IHandler
{

    ReaderWriterLockSlim rwLock = new ReaderWriterLockSlim();
    IHandler wrappedHandler;

    public ThreadSafeHandler(IHandler handler)
    {
        wrappedHandler = handler;
    }

    public void Foo()
    {
        try
        {
            rwLock.EnterReadLock();
            wrappedHandler.Foo();
        }
        finally
        {
            rwLock.ExitReadLock();
        }
    }

    public void Bar()
    {
        try
        {
            rwLock.EnterReadLock();
            wrappedHandler.Foo();
        }
        finally
        {
            rwLock.ExitReadLock();
        }
    }

    public void SwapHandler(IHandler newHandler)
    {
        try
        {
            rwLock.EnterWriteLock();
            UnhookProtocol(wrappedHandler);
            HookProtocol(newHandler);
        }
        finally
        {
            rwLock.ExitWriteLock();
        }
    }
}

Take note that this is still not thread safe if atomic operations are required on the handler's methods, then you would need to use higher order locking between treads or add methods on your wrapper class to support thread safe atomic operations (something like, BeginTreadSafeBlock() folowed by EndTreadSafeBlock() that lock the wrapped handler for writing for a series of operations.


You can't and it's logical that you can't. The best you can do is avoid any other thread from disrupting the state between those two actions (as have already been said).

Here is why you can't:

Imagine there was an block that told the operating system to never thread switch while you're on that block. That would be technically possible but will lead to starvation everywhere.

You might thing your threads are the only one being used but that's an unwise assumption. There's the garbage collector, there are the async operations that works with threadpool threads, an external reference, such as a COM object could span its own thread (in your memory space) so that noone could progress while you're at it.

Imagine you make a very long operation in your HookOperation method. It involves a lot of non leaky operations but, as the Garbage Collector can't take over to free your resources, you end up without any memory left. Or imagine you call a COM object that uses multithreading to handle your request... but it can't start the new threads (well it can start them but they never get to run) and then joins them waiting for them to finish before coming back... and therefore you join on yourself, never returning!!.


As other posters have already said, you can't enforce system-wide critical section from user-mode code. However, you don't need it to implement the hot swapping.

Here is how.

Implement a proxy with the same interface as your hot-swappable Foo object. The proxy shall call HookProtocol and never unhook (until your app is stopped). It shall contain a reference to the current Foo handler, which you can replace with a new instance when needed. The proxy shall direct the data it receives from hooked functions to the current handler. Also, it shall provide a method for atomic replacement of the current Foo handler instance (there is a number of ways to implement it, from simple mutex to lock-free).

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号