Pergunta

O código que eu quero escrever é como este:

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

Eu sei que eu posso ter o thread B verificar periodicamente, no segmento de forma segura, para ver se um sinalizador foi definido pelo segmento, mas que torna o código mais complicado.Existe um mecanismo melhor que eu posso usar?

Aqui está uma mais aprofundados exemplo de verificar periodicamente:

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.
    }
}
Foi útil?

Solução

Esta NÃO é uma boa ideia

Este artigo fala sobre ruby tempo limite de biblioteca. que lança exceções entre threads.

Ele explica como fazer tal coisa é fundamentalmente quebrado.Não é apenas quebrado em ruby, é quebrada em qualquer lugar que lança exceções entre threads.

Em poucas palavras, o que pode (e faz) acontecer é este:

ThreadA:

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

ThreadB:

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

O seu " verificação periódica o exemplo é bom, como você não está de fato jogando exceções entre threads.
Você está apenas a definição de uma bandeira que diz "lançar uma exceção a próxima vez que você olhar para esta bandeira", o que é bom não sofrem com o "pode ser jogado no meio de sua captura ou, finalmente, bloco" problema.
No entanto, se você estiver indo para fazer isso, você pode muito bem ser apenas a definição de um "exitnow" bandeira", e usando isso e salvar o trabalho de criar o objeto de exceção.Um voláteis bool vai funcionar muito bem para isso.

Outras dicas

Há problemas suficientes com exceções que podem ser geradas em threads por outros mecanismos, como a abortar threads e o que gosta, que você deve encontrar outra maneira de fazê-lo.

Uma exceção é um mecanismo usado para indicar que um processo tem experimentado algo excepcional que ele não pode lidar com.Você deve tentar evitar escrever o código para que uma exceção é utilizado para o sinal de que outra coisa já experimentou algo excepcional.

Que outro thread provavelmente não sabe como lidar com a exceção em todos os casos em que pode ser lançada por seu código.

Em suma, você deve encontrar algum outro mecanismo para abortar seu threads do que com as exceções.

Usar objetos de evento ou semelhante para contar uma thread para anular o seu processamento, que é a melhor maneira.

Ao pesquisar outro problema, me deparei com este artigo, que me lembrou de sua pergunta:

Sondando as Profundezas de ThreadAbortException usando Rotor

Ele mostra as oscilações que .LÍQUIDO passa por implementar Thread.Abort() -- provavelmente qualquer outra cruz-thread exceção teria de ser semelhante.(Yeech!)

O que Orion Edwards está dizendo que não é totalmente verdade:não é a "única" forma.

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

Usando o CER (Restrita De Execução Regiões) em C# permite a liberação de seus recursos como uma operação atômica, protegendo o seu código de inter-thread exceções.Esta técnica é usada por várias classes de .NET Framework que trabalha com Windows API nativa, onde uma inédita punho pode causar um vazamento de memória.

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

O exemplo a seguir mostra como confiável conjunto de identificadores utilizando o PrepareConstrainedRegions o método.Confiável definir um identificador para um determinado pré-identificador existente, você deve garantir que a alocação dos nativos lidar e a posterior gravação do que lidar dentro de um SafeHandle objeto é atômica.Qualquer falha entre essas operações (tais como um thread de abortar ou fora de exceção de memória) irá resultar em que o nativo lidar com o vazamento.Você pode usar o PrepareConstrainedRegions método para certificar-se de que a alça não é perdido.

Tão simples como:

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

Estou interessado em saber por que você iria querer fazer isso.Não há uma maneira fácil de fazer isso, porque não é uma boa prática.Você provavelmente deve voltar para o seu projeto e descobrir uma forma de limpeza para alcançar o objetivo final.

Eu não acho que é uma boa idéia..Tomar outra rachadura no problema - Tente usar algum outro mecanismo, como dados compartilhada para sinal entre threads.

Como os outros, eu não tenho certeza de que isso é uma boa ideia, mas se você realmente deseja fazê-lo, em seguida, você pode criar uma subclasse de SynchronizationContext, que permite a publicação e o envio de delegados para o segmento de destino (se é um WinForms thread o trabalho é feito para você, como tal, uma subclasse já existe).O segmento de destino terá para implementar algum tipo de uma bomba de mensagem equivalente que, para receber os delegados.

@Orion Edwards

Eu levo o seu ponto de vista sobre uma exceção sendo lançada no bloco finally.

No entanto, eu acho que há uma maneira - usando ainda outro segmento - de utilizar essa exceção-como-interrupção de idéia.

Thread A:

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

O Thread B:

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

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

Ou que é apenas bobagem?

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top