代码之家  ›  专栏  ›  技术社区  ›  CodeFusionMobile

如何强制多个命令在同一线程时间段中执行?

  •  0
  • CodeFusionMobile  · 技术社区  · 15 年前

    我有一个C应用程序需要在不中断数据流的情况下将数据输入流热交换到新的处理程序类。

    要做到这一点,我必须在一个线程中执行多个步骤,而不需要任何其他线程(大多数是数据接收线程)在它们之间运行,因为CPU切换。

    这是情况的简化版本,但应该说明问题。

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

    这两行(unhook和hook)必须在同一个CPU片中执行,以防止在它们之间执行另一个线程时任何数据包通过。

    如何确保这两个命令使用C线程方法正常运行?

    编辑
    似乎有些混乱,所以我会尽量更具体一些。我的意思不是同时执行,而是在同一个CPU时间片上,这样在这两个时间片完成之前就不会有线程执行。锁不是我要找的,因为这只会阻止在两个命令运行之前再次执行此代码。在这些命令完成之前,我需要防止任何线程运行。另外,我再说一次,这是我问题的简化版本,所以不要试图解决我的例子,请回答这个问题。

    7 回复  |  直到 15 年前
        1
  •  4
  •   Daniel Brückner    15 年前

    在单个时间片中执行该操作毫无帮助-该操作可以在另一个内核或处理器上并行执行,并在执行交换时访问流。当流处于不一致状态时,必须使用锁定来防止所有人访问流。

        2
  •  1
  •   Aaron    15 年前

    数据接收线程需要锁定访问处理程序指针,并且需要锁定更改处理程序指针。

    或者,如果处理程序是单个变量,则可以使用interlocked.exchange()自动交换值。

        3
  •  0
  •   Sixten Otto    15 年前

    为什么不从另一个方向着手,让有问题的线程处理交换呢?据推测,当有数据需要处理时,某些东西会被唤醒,并将其传递给当前的foo。你能给那个线程发一个通知吗,它下次唤醒时需要换一个新的处理程序?我想,这样就不用担心了。

        4
  •  0
  •   Aaron    15 年前

    好的-回答你的具体问题。

    您可以枚举进程中的所有线程,并对每个线程(活动线程除外)调用thread.suspend(),进行更改,然后调用thread.resume()。

        5
  •  0
  •   Pop Catalin    15 年前

    假设您的处理程序是线程安全的,我的建议是在处理程序上编写一个公共包装器,使用一个私有锁来完成它需要的所有锁定,这样您就可以安全地在后台更改处理程序。

    如果你这样做,你也可以使用 ReaderWriterLockSlim ,用于访问允许并发读取访问的打包处理程序。

    或者,您可以以一种不需要锁定的方式构建包装类和处理程序类,并且可以使用简单的互锁写或比较交换来完成处理程序淹没。

    示例如下:

    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();
            }
        }
    }
    

    请注意,如果需要对处理程序的方法执行原子操作,这仍然不是线程安全的,那么您需要在线程之间使用更高级别的锁定,或者在包装类上添加方法,以支持线程安全的原子操作(例如,EndTreadSafeBlock()使用的BeginReadSafeBlock(),它为为一系列操作编写。

        6
  •  0
  •   Jorge Córdoba    15 年前

    你不能,这是合乎逻辑的,你不能。你能做的最好的就是避免任何其他线程破坏这两个操作之间的状态(正如已经说过的)。

    这就是为什么你不能:

    假设有一个块告诉操作系统当您在该块上时不要线程切换。这在技术上是可能的,但会导致世界各地的饥饿。

    你可能认为你的线程是唯一被使用的,但这是一个不明智的假设。这里有垃圾收集器,这里有与线程池线程一起工作的异步操作,一个外部引用,比如一个COM对象可以跨越它自己的线程(在内存空间中),这样当你在那里的时候就不会有人进步。

    假设您在HookOperation方法中执行了非常长的操作。它涉及许多非泄漏操作,但是由于垃圾收集器无法接管以释放资源,因此最终您将失去任何内存。或者设想您调用一个使用多线程处理请求的COM对象…但它不能启动新的线程(它可以启动它们,但它们永远无法运行),然后加入它们,等待它们完成后再返回…因此,你加入自己,永不回来!!

        7
  •  0
  •   atzz    15 年前

    正如其他海报已经说过的,您不能从用户模式代码强制执行系统范围的关键部分。但是,您不需要它来实现热交换。

    这是怎么做的。

    使用与热插拔相同的接口实现代理 Foo 对象。代理人应 HookProtocol 永远不要解开(直到你的应用程序停止)。应包含对电流的参考 FOO公司 处理程序,您可以在需要时用新实例替换它。代理应将其从挂接函数接收的数据定向到当前处理程序。此外,它还应提供一种原子替换电流的方法。 处理程序实例(实现它的方法有很多种,从简单的互斥到无锁)。