views:

112

answers:

3

I have had to set a fixed time out for a particular COM method call from a service that we have (which is written in C#). Not having used the System.Threading namespace for anything other than Thread.Sleep, I have had a play and have come up with a working prototype:

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();
}

Practically speaking, are there any problems with this approach? For instance, would there be a problem if I were to abort the thread that makes the COM method call (perhaps in terms of cleaning up used resources, etc)?

+1  A: 

Yeah, big problem: it won't work in many cases. If your COM thread is busy in native code when you call Abort(), nothing will happen-it just sets a flag so when the thread comes back into managed code, it will pop the ThreadAbortException. There isn't a 100% reliable way to abort a call to unmanaged code. You can try killing the underlying OS thread, but the CLR won't respond well to that and you'll likely destabilize the process.

nitzmahone
Good point about Thread.Abort not being able to abort the native code call.
jpoh
+2  A: 

Thread.Abort() is always a problem.

Do you know anything about the COM server? Does it run in-process, out of process or remotely? If the COM server is buggy and you actually need to terminate it, consider wrapping the call in a sacrificial process (or at least a separate AppDomain) which can be terminated safely (and perhaps you can do some cheating and terminate the offending COM app as well). Don't abort threads in your own process if you can help it.

Pontus Gagge
+1  A: 

I must add to already mentioned by other commenters that waiting like

while (!_timedOut && !_comCallSuccessful) { }

is wrong, since it makes your CPU to spend its cyles stupidly.

You'd better to use 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!");
    }
}

If there's nothing to do on your main thread, you may start only one thread and use _comCallSuccessful.WaitOne(timeout), which returns true if event was Set() before timeout.

And anyway, you'd better have an explicit way to cancel operation at your service (e.g. COM object method)

elder_george
Thanks for the tip!
jpoh