Как эффективно тайм-аут вызова метода COM из кода C # - PullRequest
0 голосов
/ 02 октября 2009

Мне пришлось установить фиксированное время ожидания для конкретного вызова метода COM из имеющейся у нас службы (которая написана на C #). Не использовав пространство имен System.Threading для чего-либо, кроме Thread.Sleep, у меня была игра, и я создал рабочий прототип:

bool _comCallSuccessful = false;
bool _timedOut = false;

private void MakeACOMCallThatCouldTakeALongTime()
{
    Thread.Sleep(2500);
    _comCallSuccessful = true;
}

private void CheckForOneSecondTimeOut()
{
    Thread.Sleep(1000);
    _timedOut = true;
}

private void ThreadTester()
{
    Thread t1 = new Thread(new ThreadStart(MakeACOMCallThatCouldTakeALongTime));
    Thread t2 = new Thread(new ThreadStart(CheckForOneSecondTimeOut));

    t1.Start();
    t2.Start();

    while (!_timedOut && !_comCallSuccessful) { }

    if (_comCallSuccessful)
    {
        Console.WriteLine("Finished!");
    }
    else
    {
        t1.Abort();
        Console.WriteLine("Timed out!");
    }
    Console.ReadLine();
}

Практически, есть ли проблемы с этим подходом? Например, возникла бы проблема, если бы я прервал поток, вызывающий вызов метода COM (возможно, с точки зрения очистки использованных ресурсов и т. Д.)?

Ответы [ 3 ]

2 голосов
/ 02 октября 2009

Thread.Abort () всегда проблема .

Знаете ли вы что-нибудь о COM-сервере? Он работает в процессе, вне процесса или удаленно? Если COM-сервер глючит, и вам действительно нужно прекратить его, рассмотрите возможность упаковки вызова в жертвенный процесс (или, по крайней мере, в отдельный AppDomain), который можно безопасно завершить (и, возможно, вы можете сделать некоторые мошенничества и прекратить работу приложения-нарушителя как Что ж). Не прерывайте потоки в своем собственном процессе, если можете помочь.

1 голос
/ 02 октября 2009

Я должен добавить к уже упомянутым другим комментаторам, которые ждут, как

while (!_timedOut && !_comCallSuccessful) { }

неправильно, так как он заставляет ваш процессор тупо тратить свои циклы.

Вам лучше использовать System.Threading.EventWaitHandle:

EventwaitHandle _comCallSuccessful = new ManualResetEvent(false);
EventwaitHandle _timedOut = new ManualResetEvent(false);
private void MakeACOMCallThatCouldTakeALongTime() {
    Thread.Sleep(2500);
    _comCallSuccessful.Set();
}

private void CheckForOneSecondTimeOut() {
    Thread.Sleep(1000);
    _timedOut.Set();
}

private void ThreadTester() {
    /* thread starting*/
    var handles = new WaitHandle[]{_comCallSuccessful, _timedOut};
    int indexFirstSet = Waithandle.WaitAny(handles);
    if (indexFirstSet == 0) // _comCallSuccessful
    {
        Console.WriteLine("Finished!");
    }
    else
    {
        t1.Abort();
        Console.WriteLine("Timed out!");
    }
}

Если вам ничего не нужно делать в основном потоке, вы можете запустить только один поток и использовать _comCallSuccessful.WaitOne (timeout), который возвращает true, если событие было Set () до истечения времени ожидания.

И в любом случае, вам лучше иметь явный способ отменить операцию в вашем сервисе (например, метод COM-объекта)

1 голос
/ 02 октября 2009

Да, большая проблема: во многих случаях это не сработает. Если ваш COM-поток занят собственным кодом при вызове Abort (), ничего не произойдет - он просто устанавливает флаг, поэтому, когда поток возвращается в управляемый код, он вызывает исключение ThreadAbortException. Не существует 100% надежного способа прервать вызов неуправляемого кода. Вы можете попытаться убить основной поток ОС, но CLR не будет хорошо реагировать на это, и вы, вероятно, дестабилизируете процесс.

...