我有对象obj
,它是第三方组件,
// this could take more than 30 seconds
int result = obj.PerformInitTransaction();
我不知道里面发生了什么。 我所知道的是,如果需要更长的时间,那就失败了。
如何为此操作设置超时机制,如果超过30秒,我只是抛出MoreThan30SecondsException
?
答案 0 :(得分:72)
您可以在单独的线程中运行该操作,然后在线程连接操作上设置超时:
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.");
}
}
}
答案 1 :(得分:12)
更简单地使用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");
答案 2 :(得分:6)
如果您不想阻止主线程,可以使用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停止线程的方式( Shutting Down Worker Threads Gracefully )不如中止。
但是,由于您无法控制PerformInitTransaction()
正在执行的操作,因此当Abort失败并使对象处于无效状态时,您无法做很多事情。如上所述,如果您能够清除任何导致PerformInitTransaction
中止的内容,那么您可以通过捕获ThreadAbortException
来执行此操作,但因为它是第三方调用它将意味着猜测您的状态'我们把他们的方法留在了。
PerformInitTransaction
应该是提供超时的那个。
答案 3 :(得分:2)
你需要注意中止这样的操作,尤其是因为它(可能)没有访问要修改的代码的第三方组件。
如果您中止操作,那么您将不知道您已经离开基础类的状态。例如,它可能已获得锁定,而您的约会导致该锁定未被释放。即使您在中止操作后销毁该对象,它也可能已经改变了一些全局状态,因此您将无法在不重新启动的情况下可靠地创建新实例。
答案 4 :(得分:1)
您可能会考虑在线程中调用该方法,并在超时时中止线程并引发异常。此外,在这种情况下,您必须处理ThreadBorted异常。
答案 5 :(得分:1)
以下是两个实现,它们也会抛出内部任务中发生的任何异常。
对于操作(无返回值):
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;
}
答案 6 :(得分:0)
答案 7 :(得分:0)
这就是我要用的。与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);
}
}
答案 8 :(得分:0)
我认为这是最简单的:
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();
});
答案 9 :(得分:0)
我只是在.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();