如何为一个方法设置超时时间?

17

如何为繁忙的方法设置超时+C#。

8个回答

13

好的,这里是真正的答案。

...

void LongRunningMethod(object monitorSync)
{
   //do stuff    
   lock (monitorSync) {
     Monitor.Pulse(monitorSync);
   }
}

void ImpatientMethod() {
  Action<object> longMethod = LongRunningMethod;
  object monitorSync = new object();
  bool timedOut;
  lock (monitorSync) {
    longMethod.BeginInvoke(monitorSync, null, null);
    timedOut = !Monitor.Wait(monitorSync, TimeSpan.FromSeconds(30)); // waiting 30 secs
  }
  if (timedOut) {
    // it timed out.
  }
}

   ...

这将结合C#中最有趣的两个部分。首先,为了异步调用方法,请使用一个具有花哨的BeginInvoke魔法的委托。

然后,使用监视器从LongRunningMethodImpatientMethod发送消息,以便在完成时通知它,或者如果在一定时间内没有收到消息,则放弃它。

(附注:开玩笑,这不是真正的答案。我知道在.Net中有2^9303种解决方法,特别是剥皮猫的方法。)


4
确实可以运行,但要小心!当执行流程返回时,“LongRunningMethod”仍在后台运行!这可能会造成内存泄漏。 - Alex from Jitbit
1
这是完全正确的。如果您想停止等待某个操作,而不是停止某个操作,那么这只是一个好方法。 - MojoFilter
2
现在,我更喜欢使用Task.Wait,但这种方法适用于使用“操作系统原语”,并且可以锻炼技术术语“花哨的裤子”。 - Sully

7
除非你更改方法,否则无法做到这一点。
有两种方法:
1.该方法内置了测量其运行时间的机制,如果超过某个阈值,则会提前返回。
2.该方法内置了监视变量/事件的机制,“当设置此变量时,请退出”,然后您可以使用另一个线程来测量第一个方法中花费的时间,并在经过的时间超过某个阈值时设置该变量。
最显而易见、但不幸的是错误的答案是:“只需在线程中运行方法,并在运行时间过长时使用Thread.Abort”。
唯一正确的方法是让该方法以一种合作的方式协作,即当它运行时间过长时进行清理退出。
还有第三种方法,将方法在单独的线程上执行,但在等待其完成之后,如果需要等待的时间过长,则简单地说“我不打算等待它完成,而是直接放弃它”。在这种情况下,该方法仍将运行并最终完成,但是等待它的其他线程将放弃等待。
把第三种方法想象成打电话给某个人,要求他们在家里找到你借给他们的那本书,当你等待5分钟后,你只是说“算了”,然后挂断电话。最终,那个人会找到那本书并回到电话旁,但却发现你不再关心结果。

5
这是一个旧问题,但现在有一个更简单的解决方案:任务(Tasks)!以下是示例代码:
var task = Task.Run(() => LongRunningMethod());//you can pass parameters to the method as well
if (task.Wait(TimeSpan.FromSeconds(30)))
    return task.Result; //the method returns elegantly
else
    throw new TimeoutException();//the method timed-out

3

尽管MojoFilter的回答很好,但如果“LongMethod”冻结,它可能会导致泄漏。 如果您对结果不再感兴趣,应中止操作。

public void LongMethod()
{
    //do stuff
}

public void ImpatientMethod()
{
    Action longMethod = LongMethod; //use Func if you need a return value

    ManualResetEvent mre = new ManualResetEvent(false);

    Thread actionThread = new Thread(new ThreadStart(() =>
    {
        var iar = longMethod.BeginInvoke(null, null);
        longMethod.EndInvoke(iar); //always call endinvoke
        mre.Set();
    }));

    actionThread.Start();
    mre.WaitOne(30000); // waiting 30 secs (or less)
    if (actionThread.IsAlive) actionThread.Abort();
}

2
您可以在单独的线程中运行该方法,并监视它,如果它工作时间过长,则强制退出。一个好的方式,如果您可以这样称呼它,就是为方法开发一个属性,使用Post Sharp,这样观察代码就不会污染您的应用程序。
我已经编写了以下示例代码(请注意示例代码部分,它能够工作,但可能会遇到多线程问题,或者如果涉及到捕获ThreadAbortException的方法,则会导致其崩溃):
static void ActualMethodWrapper(Action method, Action callBackMethod)
{
    try
    {
        method.Invoke();
    } catch (ThreadAbortException)
    {
        Console.WriteLine("Method aborted early");
    } finally
    {
        callBackMethod.Invoke();
    }
}

static void CallTimedOutMethod(Action method, Action callBackMethod, int milliseconds)
{
    new Thread(new ThreadStart(() =>
    {
        Thread actionThread = new Thread(new ThreadStart(() =>
        {
            ActualMethodWrapper(method, callBackMethod);
        }));

        actionThread.Start();
        Thread.Sleep(milliseconds);
        if (actionThread.IsAlive) actionThread.Abort();
    })).Start();
}

使用以下调用:

CallTimedOutMethod(() =>
{
    Console.WriteLine("In method");
    Thread.Sleep(2000);
    Console.WriteLine("Method done");
}, () =>
{
    Console.WriteLine("In CallBackMethod");
}, 1000);

我需要提高我的代码可读性。


你仍然需要在方法中编写代码,以确保在应该完成时进行干净的退出。 - Lasse V. Karlsen
你的意思是像捕获ThreadAbort异常并清理资源/提供退出代码这样的操作吗?这实际上取决于具体的方法。 - Yuriy Faktorovich
如果他所说的“超时”是指“我想关闭我的程序,不等待方法完成”,那么可以使用Thread.Abort,但这确实是唯一的情况。 - Lasse V. Karlsen
使用示例代码,该方法可以捕获ThreadAbortException,执行例如关闭数据库连接等操作,但必须重新抛出异常。也许我对问题理解过多。 - Yuriy Faktorovich
Thread.Sleep 不是很好,因为即使工作更快完成,它也会暂停“毫秒” :) - Alex from Jitbit

0
我经常编写需要在不同平台上同步时间关键任务的应用程序。如果可以避免使用thread.abort,就应该避免。请参阅http://blogs.msdn.com/b/ericlippert/archive/2010/02/22/should-i-specify-a-timeout.aspxhttp://www.interact-sw.co.uk/iangblog/2004/11/12/cancellation以获取有关何时适用thread.abort的指南。以下是我实现的概念:
  • 选择性执行:仅在存在合理成功机会时运行(基于满足超时或成功结果相对于其他排队项的可能性)。如果将代码分成段,并知道任务块之间预期的大致时间,则可以预测是否应该跳过任何进一步处理。总时间可以通过使用递归函数进行时间计算来包装对象bin任务,或者通过拥有监视工作者以了解预期等待时间的控制器类来测量。
  • 选择性孤立:仅在存在合理成功机会时等待返回。索引任务在受管理的队列中运行。超出其超时或有风险导致其他超时的任务将被孤立,而空记录将代替它们。长时间运行的任务可以用异步调用来包装。参见示例异步调用包装程序: http://www.vbusers.com/codecsharp/codeget.asp?ThreadID=67&PostID=1
  • 条件选择:类似于选择性执行,但基于组而不是个别任务。如果您的许多任务彼此连接,以至于一个成功或失败使得额外处理无关紧要,请创建一个在开始执行之前和在长时间运行的子任务开始之前再次检查的标志。当您使用parallel.for或其他类似的队列并发任务时,这非常有用。

0

C# 中的方法没有超时限制,除非您在调试器中或操作系统认为您的应用程序已经“挂起”。即使如此,处理仍然会继续进行,只要您不杀死应用程序,就会返回响应并且应用程序会继续工作。

对数据库的调用可能会有超时限制。


0
你能否创建一个 异步方法,这样在“繁忙”方法完成时,你可以继续做其他的事情吗?

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接