C# 为操作设置超时

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2265412/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-07 00:52:50  来源:igfitidea点击:

Set timeout to an operation

c#multithreadingtimeout

提问by Anwar Chandra

I have object objwhich is 3rd party component,

我有对象obj,它是第 3 方组件,

// this could take more than 30 seconds
int result = obj.PerformInitTransaction(); 

I don't know what is happening inside. What I know is if it take longer time, it is failed.

我不知道里面发生了什么。我所知道的是,如果需要更长的时间,它就会失败。

how to setup a timeout mechanism to this operation, so that if it takes more than 30 seconds I just throw MoreThan30SecondsException?

如何为此操作设置超时机制,以便如果超过 30 秒我就抛出MoreThan30SecondsException

采纳答案by Paolo Tedesco

You could run the operation in a separate thread and then put a timeout on the thread join operation:

您可以在单独的线程中运行该操作,然后在线程连接操作上设置超时:

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.");
        }
    }
}

回答by Kangkan

You might look at invoking the method in a thread and upon the timeout, abort the thread and raise the exception. Also, you shall have to handle the ThreadBorted Exception in this case.

您可能会考虑在线程中调用该方法,并在超时时中止线程并引发异常。此外,在这种情况下,您必须处理 ThreadBorted 异常。

回答by BrianB

There is a nice example of a generic solution to this using a helper class here.

有一个很好的例子,说明了使用帮助类here的通用解决方案。

It uses the Action delegate to avoid the Thread creation/destruction shown in the previous example.

它使用 Action 委托来避免上一个示例中显示的 Thread 创建/销毁。

I hope this helps.

我希望这有帮助。

回答by Sean

You need to be careful about aborting an operation like this, especially as it's in a 3rd party component that you (possibly) don't have access to the code to modify.

您需要小心中止这样的操作,尤其是因为它位于您(可能)无权访问要修改的代码的第 3 方组件中。

If you abort the operation then you won't know what state you've left the underlying class in. For example, it may have acquired a lock, and your about has caused that lock to not be released. Even if you destroy the object after aborting the operation it may have altered some state that is global to it and therefore you won't be able to reliably create a new instance without a restart.

如果您中止操作,那么您将不知道底层类处于什么状态。例如,它可能获得了一个锁,而您的 about 导致该锁没有被释放。即使您在中止操作后销毁对象,它也可能改变了一些对其全局的状态,因此您将无法在不重新启动的情况下可靠地创建新实例。

回答by Chris S

If you don't want to block the main thread you can use a System.Threading.Timer:

如果你不想阻塞主线程,你可以使用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 has a less forceful way (Shutting Down Worker Threads Gracefully) of stopping the thread than abort.

与中止相比,Jon Skeet 有一种不那么有力的停止线程的方法(优雅关闭工作线程)。

However as you're not in control of the operations PerformInitTransaction()is doing there is not much you can do from when Abort fails and leaves the object in an invalid state. As mentioned if you are able to cleanup anything that aborting the PerformInitTransactionhas left hanging, you can do this by catching the ThreadAbortException, though as it's a 3rd party call it'll mean guessing the state you've left their method in.

但是,由于您无法控制PerformInitTransaction()正在执行的操作,因此当 Abort 失败并使对象处于无效状态时,您无能为力。如前所述,如果您能够清除任何中止PerformInitTransaction挂起的东西,您可以通过捕获 来做到这一点ThreadAbortException,尽管这是一个 3rd 方调用,这意味着猜测您将其方法留在的状态。

The PerformInitTransactionshould really be the one providing the timeout.

PerformInitTransaction确实应该是一个提供超时。

回答by Colonel Panic

More simply using Task.Wait(TimeSpan):

更简单地使用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");

回答by Mike

this is what I would use. works similar to how a javascript timeout works.

这就是我会使用的。工作原理类似于 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);
    }

}

回答by Gilad Rave

The following are two implementations which also throw any exception that happens in the internal task.

以下是两个实现,它们也会抛出在内部任务中发生的任何异常。

For actions (no return value):

对于操作(无返回值):

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

For Funcs (with return value):

对于 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;
}

回答by Ariful Islam

I think this is simplest of all:

我认为这是最简单的:

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

回答by user875234

I just ran into this in a .NET 4.0 app (no access to Task.Run, Task.Delay, etc.). If you will excuse the last line (which is the setTimeout part) it's fairly concise.

我只是碰到了这个在.NET 4.0的应用程序(用不上Task.RunTask.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();