2016-01-13 5 views
4

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

using (var connection = (IConnection) Factory.GetObject(typeof (IConnection))) 

Код, используемый для работы прекрасно. Но за последние несколько минут казалось, что моя программа ничего не делает, когда я пытался ее запустить. Нажатие паузы во время отладки показало мне, что она «застряла» на линии выше.
Я предполагаю, что они просто выполняют какое-то обслуживание или что-то в этом роде, но здесь дело не в этом.
Так что, хотя было бы неплохо сообщить пользователю, что пошло не так, если программа не запускается. Что-то простое, как

MessageBox.Show("Could not connect", "Connection Error"); 

И затем закройте программу. Мой вопрос:
Как завершить выполнение команды через определенное время и перепрыгнуть куда-нибудь еще?
Мое предположение будет перемещать его в отдельный поток, а затем помещать вызывающий поток в спящий режим на несколько секунд, после чего он избавляется от лишнего потока, если он еще не завершен. Но это кажется действительно грязным для меня, и там должен быть лучший способ.

+0

Если ваш 'IConnection' является' SqlConnection', вы можете использовать таймаут соединения по умолчанию, предоставляемый вместе с ним. –

+0

@ Kilanny, хотя использование очень похоже, это не SqlConnection, и никто, похоже, не пытался реализовать тайм-аут. – Wilsu

+0

В противном случае используйте «Таймер», чтобы проверить выполнение, когда тайм-аут должен прекратить работу –

ответ

4

Ваш вопрос можно разделить на две части:

  1. Как оканчиваются выполнение команды?

Единственный способ - прервать нить. Но не делайте этого. Нет гарантированного и безопасного пути. Существуют такие методы, как Thread.Interrupt и Thread.Abort, которые могут разбудить поток. Но они будут работать только в том случае, если поток находится в состоянии WaitSleepJoin, и он зависает в управляемом коде.

Похоже, вы уже это знаете. Но еще раз, если что-то внутри сборки бесконечно вешает выполнение кода, то поток, вероятно, «ушел». Поэтому вы правы, что программа должна быть закрыта.

  1. ... прыгать в другое место?

Хороший подход - использование TPL и асинхронной модели. Ниже приведен метод расширения для завершения любой задачи и истекает после таймаута.

public static async Task TimeoutAfter(this Task task, int millisecondsTimeout) 
{ 
    if (task == await Task.WhenAny(task, Task.Delay(millisecondsTimeout))) 
     await task; 
    else 
     throw new TimeoutException(); 
} 

Затем используйте его

try 
{ 
    using (var result = await Task.Run(() => (IConnection)Factory.GetObject(typeof(IConnection))).TimeoutAfter(1000)) 
    { 
     ... 
    } 
} 
catch (TimeoutException ex) 
{ 
    //timeout 
} 

Here вы можете найти более подробную информацию

+0

Подробный ответ, дающий указания еще более подробно. Я люблю это. Спасибо. – Wilsu

2

Самый простой способ сделать это, если собственный тайм-аут не реализован, как вы упомянули, является отдельным потоком для его загрузки. Хотя это звучит, как это будет на самом деле грязная, это так просто, как (с помощью Rx):

Task<IConnection> connectionTask = Observable.Start(() => Factory.GetObject(typeof (IConnection)), Scheduler.Default).Timeout(TimeSpan.FromSeconds(20)).ToTask()); 
using (var connection = connectionTask.Result) 
{ 
    ... 
} 

Вы можете настроить Scheduler, если вы не хотите, чтобы работать на ThreadPool. Он выкинет TimeoutException, если вызов Factory.GetObject занимает больше 20 секунд.

+0

Такая же идея, но гораздо более гладкая. Большое спасибо. Я окружил его «try-catch», но когда я пытаюсь отлаживать, он говорит, что никогда не достигнет точки останова, которую я разместил внутри 'catch'. Кажется странным для меня, завтра я еще раз посмотрю на это. – Wilsu

3

Простой способ сделать это без дополнительных библиотек или методов расширения:

using (var task = new Task<IConnection>(() => Factory.GetObject(typeof(IConnection)))) 
{ 
    task.Start(); 

    if(!task.Wait(timeoutMilliseconds)) 
    { 
     throw new TimeoutException(); 
    } 

    IConnection result = task.Result; 
} 

Task.Wait делает то, вы хотите, потому что вы можете выбросить исключение, если оно оказывается ложным (задача не была завершена вовремя.)

Это даже проще, если у вас есть действие, которое не возвращает что-то:

if (!Task.Run(action).Wait(timeoutMilliseconds)) 
{ 
    throw new TimeoutException(); 
} 

Где action некоторые Action или лямбда.

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