为操作设置超时
Posted
技术标签:
【中文标题】为操作设置超时【英文标题】:Set timeout to an operation 【发布时间】:2011-01-16 22:46:49 【问题描述】:我有对象obj
,它是第 3 方组件,
// this could take more than 30 seconds
int result = obj.PerformInitTransaction();
我不知道里面发生了什么。 我知道如果需要更长的时间,它就会失败。
如何为此操作设置超时机制,这样如果超过 30 秒我就抛出 MoreThan30SecondsException
?
【问题讨论】:
【参考方案1】:您可以在单独的线程中运行该操作,然后在线程连接操作上设置超时:
using System.Threading;
class Program
static void DoSomething()
try
// your call here...
obj.PerformInitTransaction();
catch (ThreadAbortException)
// cleanup code, if needed...
public static void Main(params string[] args)
Thread t = new Thread(DoSomething);
t.Start();
if (!t.Join(TimeSpan.FromSeconds(30)))
t.Abort();
throw new Exception("More than 30 secs.");
【讨论】:
@Bomboca:我回滚了你的编辑,我抛出的Exception
不应该是 ThreadAbortException
,这是 CLR 在调用 Abort
时抛出的东西。
这是一个阻塞调用,如果你此时需要主线程做其他事情,这是行不通的!【参考方案2】:
更简单地使用Task.Wait(TimeSpan)
:
using System.Threading.Tasks;
var task = Task.Run(() => obj.PerformInitTransaction());
if (task.Wait(TimeSpan.FromSeconds(30)))
return task.Result;
else
throw new Exception("Timed out");
【讨论】:
这是非常简单的,从 .net 4.0 开始就可以使用了。 @anwar 我会再次遇到这个答案,我正在使用 .NET Framework 4.0,所以请让我纠正你:Task.Run 从 .NET 4.5 开始可用。 link 这不会结束线程。建议与 CancellationToken 一起使用。【参考方案3】:如果你不想阻塞主线程,你可以使用System.Threading.Timer:
private Thread _thread;
void Main(string[] args)
_thread = new ThreadStart(ThreadEntry);
_thread.Start();
Timer timer = new Timer(Timeout,null,30000,Timeout.Infinite);
void ThreadEntry()
int result = obj.PerformInitTransaction();
void TimeOut(object state)
// Abort the thread - see the comments
_thread.Abort();
throw new ItTimedOutException();
Jon Skeet 有一种比 abort 更不有力的方式来停止线程 (Shutting Down Worker Threads Gracefully)。
但是,由于您无法控制 PerformInitTransaction()
正在执行的操作,因此当 Abort 失败并使对象处于无效状态时,您无能为力。如前所述,如果您能够清理中止 PerformInitTransaction
挂起的任何内容,您可以通过捕获 ThreadAbortException
来执行此操作,但由于它是第 3 方调用,这意味着猜测您已经离开他们的方法的状态在。
PerformInitTransaction
确实应该是提供超时的那个。
【讨论】:
文章“优雅地关闭工作线程”的链接已损坏。我可以建议jonskeet.uk/csharp/threads/shutdown.html【参考方案4】:以下是两个实现,它们也会抛出内部任务中发生的任何异常。
对于动作(无返回值):
public static bool DoWithTimeout(Action action, int timeout)
Exception ex = null;
CancellationTokenSource cts = new CancellationTokenSource();
Task task = Task.Run(() =>
try
using (cts.Token.Register(Thread.CurrentThread.Abort))
action();
catch (Exception e)
if (!(e is ThreadAbortException))
ex = e;
, cts.Token);
bool done = task.Wait(timeout);
if (ex != null)
throw ex;
if (!done)
cts.Cancel();
return done;
对于 Funcs(带返回值):
public static bool DoWithTimeout<T>(Func<T> func, int timeout, out T result)
Exception ex = null;
result = default(T);
T res = default(T);
CancellationTokenSource cts = new CancellationTokenSource();
Task task = Task.Run(() =>
try
using (cts.Token.Register(Thread.CurrentThread.Abort))
res = func();
catch (Exception e)
if (!(e is ThreadAbortException))
ex = e;
, cts.Token);
bool done = task.Wait(timeout);
if (ex != null)
throw ex;
if (done)
result = res;
else
cts.Cancel();
return done;
【讨论】:
【参考方案5】:我认为这是最简单的:
using System.Threading.Tasks;
var timeToWait = 30000; //ms
Task.Run(async () =>
await Task.Delay(timeToWait);
//do your timed task i.e. --
int result = obj.PerformInitTransaction();
);
【讨论】:
【参考方案6】:您需要小心中止这样的操作,尤其是当它位于您(可能)无法访问要修改的代码的第 3 方组件中时。
如果您中止操作,那么您将不知道您将底层类置于什么状态。例如,它可能已经获得了一个锁,而您的 about 导致该锁没有被释放。即使您在中止操作后销毁该对象,它也可能改变了它的某些全局状态,因此您将无法在不重新启动的情况下可靠地创建新实例。
【讨论】:
【参考方案7】:您可能会考虑在线程中调用方法,并在超时时中止线程并引发异常。此外,在这种情况下,您必须处理 ThreadBorted 异常。
【讨论】:
【参考方案8】:有一个很好的通用解决方案示例,它使用帮助器类here。
它使用 Action 委托来避免前面示例中显示的线程创建/销毁。
我希望这会有所帮助。
【讨论】:
【参考方案9】:这是我会使用的。工作方式类似于 javascript 超时的工作方式。
public class Toolz
public static System.Threading.Tasks.Task<object> SetTimeout(Func<object> func, int secs)
System.Threading.Thread.Sleep(TimeSpan.FromSeconds(secs));
return System.Threading.Tasks.Task.Run(() => func());
class Program
static void Main(string[] args)
Console.WriteLine(DateTime.Now);
Toolz.SetTimeout(() =>
Console.WriteLine(DateTime.Now);
return "";
, 10);
【讨论】:
【参考方案10】:我刚刚在 .NET 4.0 应用程序中遇到了这个问题(无法访问 Task.Run
、Task.Delay
等)。如果你会原谅最后一行(即 setTimeout 部分),它相当简洁。
int sleepTime = 10000;
Action myAction = () =>
// my awesome cross-thread update code
this.BackColor = Color.Red;
;
new System.Threading.Thread(() => System.Threading.Thread.Sleep(sleepTime); if (InvokeRequired) myAction(); else myAction(); ).Start();
【讨论】:
以上是关于为操作设置超时的主要内容,如果未能解决你的问题,请参考以下文章
redis基本操作,基于StringRedisTemplate,存储,取值,设置超时时间,获取超时时间,插入list操作