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

在C#中是否有一种好方法可以在给定线程上抛出异常

  •  11
  • fryguybob  · 技术社区  · 18 年前

    我想写的代码如下:

    void MethodOnThreadA()
    {
        for (;;)
        {
            // Do stuff
            if (ErrorConditionMet)
                ThrowOnThread(threadB, new MyException(...));
        }
    }
    
    void MethodOnThreadB()
    {
        try
        {
            for (;;)
            {
                // Do stuff
            }
        }
        catch (MyException ex)
        {
            // Do the right thing for this exception.
        }
    }
    

    下面是一个更加充实的定期检查示例:

    Dictionary<Thread, Exception> exceptionDictionary = new Dictionary<Thread, Exception>();
    
    void ThrowOnThread(Thread thread, Exception ex)
    {
        // the exception passed in is going to be handed off to another thread,
        // so it needs to be thread safe.
        lock (exceptionDictionary)
        {
            exceptionDictionary[thread] = ex;
        }
    }
    
    void ExceptionCheck()
    {
        lock (exceptionDictionary)
        {
            Exception ex;
            if (exceptionDictionary.TryGetValue(Thread.CurrentThread, out ex))
                throw ex;
        }
    }
    
    void MethodOnThreadA()
    {
        for (;;)
        {
            // Do stuff
            if (ErrorConditionMet)
                ThrowOnThread(threadB, new MyException(...));
        }
    }
    
    void MethodOnThreadB()
    {
        try
        {
            for (;;)
            {
                // Do stuff
                ExceptionCheck();
            }
        }
        catch (MyException ex)
        {
            // Do the right thing for this exception.
        }
    }
    
    8 回复  |  直到 18 年前
        1
  •  10
  •   Lasse V. Karlsen    18 年前

    其他机制(如中止线程等)可能会在线程上引发足够多的异常问题,因此您应该找到另一种方法。

    异常是一种机制,用于表示进程遇到了无法处理的异常情况。您应该尽量避免编写代码,以便使用异常来表示 别的

    另一个线程很可能不知道如何处理异常 在所有可能被代码抛出的情况下 .

    使用事件对象或类似对象通知线程中止其处理,这是最好的方法。

        2
  •  10
  •   Orion Edwards    18 年前

    这不是一个好主意

    This article talks about ruby's timeout library. 它跨线程抛出异常。

    它解释了做这样的事情是如何从根本上被打破的。它不仅在ruby中被破坏,而且在线程间抛出异常的任何地方都被破坏。

    ThreadA:

    At some random time, throw an exception on thread B:
    

    try {
        //do stuff
    } finally {
        CloseResourceOne();
        // ThreadA's exception gets thrown NOW, in the middle 
        // of our finally block and resource two NEVER gets closed.
        // Obviously this is BAD, and the only way to stop is to NOT throw
        // exceptions across threads
        CloseResourceTwo();
    }
    


    你只是设置了一个标志,上面写着“下一次看这个标志时抛出一个异常”,这很好,因为它不会遭受“在你的中间被抛出或最终阻塞”的问题。
    但是,如果您要这样做,您最好只设置一个“exitnow”标志,并使用它,从而省去创建异常对象的麻烦。一个不稳定的bool就可以了。

        3
  •  2
  •   GenericProgrammer    16 年前

    猎户座爱德华兹所说的并不完全正确:这不是唯一的方式。

    // Obviously this is BAD, and the only way to stop is to NOT throw
    // exceptions across threads
    

    使用CER( )在C#中,允许您以原子操作的形式释放资源,保护代码不受线程间异常的影响。NET Framework的几个类使用这种技术,这些类使用Windows的本机API,未释放的句柄可能会导致内存泄漏。

    http://msdn.microsoft.com/en-us/library/system.runtime.compilerservices.runtimehelpers.prepareconstrainedregions.aspx

    下面的示例演示如何使用 PrepareConstrainedRegions 方法要将句柄可靠地设置为指定的预先存在的句柄,必须确保在 SafeHandle 对象是原子的。这些操作之间的任何故障(如线程中止或内存不足异常)都将导致本机句柄泄漏。你可以使用 预备应变区 方法以确保句柄不会泄漏。

    public MySafeHandle AllocateHandle()
    {
        // Allocate SafeHandle first to avoid failure later.
        MySafeHandle sh = new MySafeHandle();
    
        RuntimeHelpers.PrepareConstrainedRegions();
        try { }
        finally  // this finally block is atomic an uninterruptible by inter-thread exceptions
        {
            MyStruct myStruct = new MyStruct();
            NativeAllocateHandle(ref myStruct);
            sh.SetHandle(myStruct.m_outputHandle);
        }
    
        return sh;
    }
    
        4
  •  1
  •   Eric    18 年前

    在研究另一个问题时,我看到了这篇文章,它让我想起了你的问题:

    Plumbing the Depths of the ThreadAbortException using Rotor

    它显示了.NET实现Thread.Abort()所经历的旋转过程——可能任何其他跨线程异常都必须是类似的。(耶!)

        5
  •  0
  •   Eric Z Beard    18 年前

    我很想知道你为什么要这么做。这不是一个简单的方法,因为这不是一个好的做法。您可能应该回到您的设计,找出一种更干净的方法来实现最终目标。

        6
  •  0
  •   Gishu    18 年前

    我认为那不是个好主意。。 在这个问题上再做一次尝试——尝试使用一些其他机制,比如共享数据来在线程之间发送信号。

        7
  •  0
  •   On Freund    18 年前

        8
  •  0
  •   Rob    18 年前

    @猎户座爱德华兹

    我接受你关于在finally块中抛出异常的观点。

    然而,我认为有一种方法——使用另一个线程——将此异常用作中断思想。

    线程A:

    At some random time, throw an exception on thread C:
    

    try {
        Signal thread C that exceptions may be thrown
        //do stuff, without needing to check exit conditions
        Signal thread C that exceptions may no longer be thrown
    }
    catch {
        // exception/interrupt occurred handle...
    }
    finally {
        // ...and clean up
        CloseResourceOne();
        CloseResourceTwo();
    }
    

    线程C:

     while(thread-B-wants-exceptions) {
            try {
                Thread.Sleep(1) 
            }
            catch {
                // exception was thrown...
                if Thread B still wants to handle exceptions
                    throw-in-B
            }
        }
    

    还是说这很愚蠢?

    推荐文章