У меня есть объект obj
который является сторонним компонентом,
// this could take more than 30 seconds
int result = obj.PerformInitTransaction();
Я не знаю то, что происходит внутри. То, что я знаю, - то, если это занимает время, это является отказавшим.
как установить механизм тайм-аута к этой операции, так, чтобы, если требуется больше чем 30 секунд, я просто бросил MoreThan30SecondsException
?
Вы можете запустить операцию в отдельном потоке, а затем установить тайм-аут для операции соединения потока:
using System.Threading;
class Program {
static void DoSomething() {
try {
// your call here...
obj.PerformInitTransaction();
} catch (ThreadAbortException) {
// cleanup code, if needed...
}
}
public static void Main(params string[] args) {
Thread t = new Thread(DoSomething);
t.Start();
if (!t.Join(TimeSpan.FromSeconds(30))) {
t.Abort();
throw new Exception("More than 30 secs.");
}
}
}
Вы должны быть осторожны при прерывании подобной операции, особенно потому, что она находится в стороннем компоненте, который (возможно) не имеет доступа к коду для изменения.
Если вы прервете операцию, то вы не узнаете, в каком состоянии вы оставили базовый класс. Например, он мог получить блокировку, и ваш about привел к тому, что эта блокировка не была снята. Даже если вы уничтожите объект после прерывания операции, он может изменить какое-то глобальное для него состояние, и поэтому вы не сможете надежно создать новый экземпляр без перезапуска.
Если вы не хотите блокировать основной поток, вы можете использовать System.Threading.Timer:
private Thread _thread;
void Main(string[] args)
{
_thread = new ThreadStart(ThreadEntry);
_thread.Start();
Timer timer = new Timer(Timeout,null,30000,Timeout.Infinite);
}
void ThreadEntry()
{
int result = obj.PerformInitTransaction();
}
void TimeOut(object state)
{
// Abort the thread - see the comments
_thread.Abort();
throw new ItTimedOutException();
}
Jon Skeet предлагает менее принудительный способ (Shutting Down Worker Threads Gracefully) остановки потока, чем abort.
Однако, поскольку вы не контролируете операции, которые выполняет PerformInitTransaction()
, вы мало что можете сделать, когда Abort не работает и оставляет объект в недопустимом состоянии. Как уже говорилось, если вы можете очистить все, что осталось после прерывания PerformInitTransaction()
, вы можете сделать это, поймав ThreadAbortException()
, хотя, поскольку это сторонний вызов, это означает угадать состояние, в котором вы оставили их метод.
На самом деле таймаут должен обеспечивать PerformInitTransaction
.
Есть хороший пример общего решения этой проблемы с использованием вспомогательного класса здесь.
Он использует делегат Action, чтобы избежать создания/уничтожения потоков, показанных в предыдущем примере.
Надеюсь, это поможет.
Вы можете посмотреть на вызов метода в потоке и по истечении времени ожидания, прервать поток и вызвать исключение. Кроме того, в этом случае вам придется обработать исключение ThreadBorted.