代码之家  ›  专栏  ›  技术社区  ›  jpoh

如何有效地超时C代码的COM方法调用

  •  0
  • jpoh  · 技术社区  · 15 年前

    我必须为我们所拥有的服务(用C语言编写)中的特定COM方法调用设置一个固定的超时。没有使用 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 回复  |  直到 15 年前
        1
  •  2
  •   Pontus Gagge    15 年前

    线程() is always a problem .

    你知道COM服务器的情况吗?它是在进程内、进程外还是远程运行?如果COM服务器有问题,而您实际上需要终止它,请考虑将调用包装在一个可以安全终止的牺牲过程(或者至少是一个单独的AppDomain)中(也许您还可以进行一些欺骗并终止有问题的COM应用程序)。如果可以帮助的话,不要在自己的进程中中止线程。

        2
  •  1
  •   nitzmahone    15 年前

    是的,大问题:在很多情况下都不起作用。如果在调用abort()时COM线程正忙于本机代码,则不会发生任何事情,它只是设置了一个标志,因此当线程返回托管代码时,它将弹出threadabortexception。没有一种100%可靠的方法可以中止对非托管代码的调用。您可以尝试终止底层的OS线程,但是clr不会对此做出很好的响应,您可能会破坏进程的稳定性。

        3
  •  1
  •   elder_george    15 年前

    我必须补充一下其他评论者已经提到的等待

    while (!_timedOut && !_comCallSuccessful) { }
    

    是错误的,因为它让你的CPU愚蠢地花费它的柱面。

    最好使用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!");
        }
    }
    

    如果您的主线程上没有任何操作,则只能启动一个线程并使用comcallssuccessful.waitone(timeout),如果在超时之前设置了event(),则返回true。

    无论如何,您最好有一个明确的方法来取消服务上的操作(例如,COM对象方法)