2014-10-31 2 views
1

Я использую этот код, чтобы дать лимит времени для другого потока и прервать его по истечении определенного времени:Получение текущей строки из другой управляемой нити

void RunWithTimeout(Action action, TimeSpan timeout) 
    { 
     AutoResetEvent signal = new AutoResetEvent(false); 
     Thread workerThread = null; 
     ThreadPool.QueueUserWorkItem((o) => 
     { 
      workerThread = Thread.CurrentThread; 
      action(); 
      signal.Set(); 
     }); 

     using (new Timer((o) => { signal.Set(); }, null, (int)timeout.TotalMilliseconds, Timeout.Infinite)) 
     { 
      signal.WaitOne(); 
     } 

     if (workerThread != null && workerThread.IsAlive) 
     { 
      try 
      { 
       workerThread.Abort(); 
      } 
      catch { } 
      throw new System.TimeoutException(); 
     } 
    } 

Я использую .NET 3.5, так что я не могу use Tasks Я бросаю TimeoutException сейчас, но я хотел бы знать строку, которая выполнялась при вызове Abort.

Есть ли способ получить стопку вызовов другого потока и передать его в свойствоисключения?

+1

Хм, довольно нездоровая предположить, что существует на самом деле * есть * а «текущая строка» когда код должен быть прерван. Он почти наверняка похоронен во внутренней функции CLR или операционной системы. Вид, необходимый для того, чтобы потоки зависали и не выполняли свою работу вовремя. Отмена потоков опасна, в ней есть миллион предупреждений. –

+1

Отмена нитей настолько чревата ошибкой, что вы не должны этого делать, кроме как в тяжелых обстоятельствах. Если у вас есть контроль над кодом, вы должны написать поток, чтобы поддержать некоторые способы совместной отмены. В .NET 4.0 вы можете использовать «CancellationToken». В более ранних версиях используйте 'WaitHandle'. См. Мой пост в блоге, [Опрос для отмены] (http://blog.mischel.com/2013/05/07/polling-for-cancellation/) –

ответ

2

Для ожидания другого потока вы не нуждаетесь в Timer, вы можете использовать перегрузку WaitOne, которая принимает таймаут в миллисекундах.

Для захвата трассировки стека на рабочем потоке, вы можете передать ThreadAbortException в основной поток через переменную закрытия:

private static void RunWithTimeout(Action action, TimeSpan timeout) 
{ 
    Thread worker = null; 
    var signal = new AutoResetEvent(false); 
    ThreadAbortException abortException = null; 
    ThreadPool.QueueUserWorkItem(o => 
    { 
     worker = Thread.CurrentThread; 
     try 
     { 
      action(); 
      signal.Set(); 
     } 
     catch (ThreadAbortException ex) 
     { 
      //thread is being aborted, pass the exception back to the main thread 
      abortException = ex; 
     } 
    }); 

    if (!signal.WaitOne(timeout)) 
    { 
     worker.Abort(); 
     //abortException is now filled from the worker thread, the stack trace for the 
     //worker thread is now inside the InnerException of the ThreadAbortException 
     throw new TimeoutException("Operation timed out", abortException); 
    } 
} 
+0

Да, 'WaitOne', а не таймер. Кстати, есть [Перегрузка WaitOne] (http://msdn.microsoft.com/en-us/library/cc190477 (v = vs.110) .aspx), которая принимает «TimeSpan». Поэтому вы можете написать 'WaitOne (timeout)', а не сначала конвертировать в миллисекунды. –

+0

@JimMischel хороший звонок, обновленный ответ – Bas

1

Вы можете попробовать с ловлей ThreadAbortException, как показано ниже:

class Program 
{ 
    static void Main(string[] args) 
    { 
     var thread = new Thread(Work); 
     thread.Start(); 
     Thread.Sleep(3000); 
     thread.Abort(); 
    } 

    static void Work() 
    { 
     try 
     { 
      while (true) 
      { 
       // do work here 
      } 
     } 
     catch (ThreadAbortException ex) 
     { 
      Console.WriteLine(ex.StackTrace); 
      // TODO: pass ex.StackTrace to main thread 
     } 
    } 
} 

трассировка стека будет включать в себя имя метода, в котором нить была, когда она была прервана. ThreadAbortException будет автоматически восстановлен, поэтому поток все равно прерван.

Смежные вопросы