2009-09-11 76 views

回答

12

好的,这是真正的答案。

... 

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#的两个最有趣的部分。首先,要异步调用该方法,请使用具有花式魔术裤的代表。

然后,使用监视器将消息从LongRunningMethod发送回ImpatientMethod,以告知消息何时完成,或者是否在一定时间内未收到消息,请放弃。

(PS-只是开玩笑这是真正的答案。我知道有2^9303的方式皮肤猫,尤其是在.NET)

+0

这对我有用:) – 2013-09-11 10:55:37

+4

它确实有效,但!请注意! - 当执行流程返回时,“LongRunningMethod”仍然在后台运行!这可能是泄漏。 – Alex 2013-09-29 21:34:01

+0

这是完全正确的。如果你想停止等待一个动作,而不是停止一个动作,这是很好的。 – MojoFilter 2013-10-03 19:48:15

7

你不能这样做,除非你改变方法。

有两种方式:

  1. 的方法是建立在其本身的尺寸了多长时间运行的时间,如果超过某个阈值,然后返回过早这种方式。
  2. 该方法的构建方式是监视一个变量/事件,该变量说明“何时设置此变量,请退出”,然后您有另一个线程测量第一个方法花费的时间,然后设置当经过的时间超过某个阈值时变量。

最明显的,但不幸的是,你可以在这里得到的答案是“只要在线程中运行该方法,并在运行时间过长时使用Thread.Abort”。

唯一正确的方法是让方法合作,以便在运行时间过长时进行干净的退出。

还有第三种方法,您可以在单独的线程上执行该方法,但在等待完成后需要很长时间,您只需说“我不会等待它完成,但只是丢弃它“。在这种情况下,该方法仍然会运行,并最终完成,但等待它的其他线程将简单放弃。

想想第三种方式叫某人,并要求他们在他们的房子里搜索你借给他们的那本书,等到电话结束5分钟后,你只需说出“aw,chuck it”,然后挂断。最终,其他人会找到这本书,然后回到电话上,只是注意到你不再关心结果。

0

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

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

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

我需要在我的代码可读性上工作。

+0

您仍然需要编写代码添加到方法做一个干净的出口时,它要完成。 – 2009-09-11 12:49:00

+0

你的意思是像抓住ThreadAbort异常和清理资源/提供退出代码?这真的取决于所讨论的方法。 – 2009-09-11 13:06:20

+0

如果通过“超时”,他的意思是“我想关闭我的程序,不要等待方法完成”,那么是的,可以使用Thread.Abort,但这是唯一的场景。 – 2009-09-11 13:44:01

0

您可以创建一个Asynchronous Method,以便您可以在“繁忙”方法完成时继续执行其他任务吗?

3

虽然MojoFilter's answer是很好,如果“它可能导致泄漏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(); 
} 
0

我经常编写应用程序,必须跨平台同步时间关键型任务。如果你可以避免thread.abort,你应该。有关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。下面是我实现的概念:

  • 选择性执行:只有当一个合理的成功机会存在(基于满足超时或相对于其他排队的项目成功结果的可能性的能力)上运行。如果将代码分解为段并大致了解任务块之间的预期时间,则可以预测是否应该跳过任何进一步处理。总时间可以通过用时间计算递归函数包装对象仓任务来测量,或者通过让控制器类监视工作人员知道预期的等待时间来测量。
  • 选择性孤儿:如果存在合理的成功机会,请等待回报。索引的任务在受管队列中运行。超过其超时或导致其他超时风险的任务将被孤立,并返回空记录。长时间运行的任务可以包装在异步调用中。请参阅示例异步调用封装器:http://www.vbusers.com/codecsharp/codeget.asp?ThreadID=67&PostID=1
  • 有条件选择:类似于选择性执行,但基于组而不是单个任务。如果许多任务互相连接,以至于一次成功或失败都会导致其他处理无关紧要,请创建一个在开始执行前检查的标志,并在长时间运行的子任务开始前再次执行检查。当你使用parallel.for或其他这样的排队并发任务时,这是特别有用的。
3

这是一个老问题,但它有一个更简单的解决方案,现在不可用:任务!

这里是一个示例代码:

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