如果处理时间太长,请取消请求

时间:2011-05-17 18:25:59

标签: c#

你好,这可以在c#中找到:

如果我有一个循环说:

int x = 0;
for (int i = 0; i < 1000000; i++) {
  x++;
}

完成时间超过1秒,是否可以终止代码并继续前进?

5 个答案:

答案 0 :(得分:5)

是的,如果您在不同的线程中运行循环,则可以从其他线程中止该线程。这将有效地杀死您正在运行的代码,并且在您给出的简化示例中可以正常工作。以下代码演示了如何执行此操作:

void Main()
{
    double foo = 0;
    var thread = new Thread(() => foo = GetFoo());
    thread.Start();
    string message;
    if(thread.Join(1000))
    {
        message = foo.ToString();
    }
    else 
    {
        message = "Process took too long";
        thread.Abort();
    }
    message.Dump();
}

public double GetFoo() {
    double a = RandomProvider.Next(2, 5); 
    while (a == 3) { RandomProvider.Next(2, 5);} 
    double b = RandomProvider.Next(2, 5); 
    double c = b /a; 
    double e = RandomProvider.Next(8, 11); 
    while (e == 9) { RandomProvider.Next(8,11); } 
    double f = b / e;
    return f;
}

然而,正如你可以从Eric Lippert的评论(和其他人)中看到的那样,中止一个帖子不是很优雅(a.k.a。“纯粹的邪恶”)。如果你在这个循环中发生了重要的事情,你可能会通过在循环中间强制线程中止来使数据处于不稳定状态。所以它是可能的,但是你想要使用这种方法将取决于你在循环中做了什么。

所以更好的选择是让你的循环在被告知时自愿决定退出(更新2015/11/5以使用更新的TPL类):

async void Main()
{
    try
    {
        var cancelTokenSource = new CancellationTokenSource(TimeSpan.FromMilliseconds(1000));
        var foo = await GetFooAsync(cancelTokenSource.Token);
        Console.WriteLine(foo);
    }
    catch (OperationCanceledException e)
    {
        Console.WriteLine("Process took too long");
    }
}

private Random RandomProvider = new Random();

public async Task<double> GetFooAsync(CancellationToken cancelToken) {
    double a = RandomProvider.Next(2, 5); 
    while (a == 3) 
    { 
        cancelToken.ThrowIfCancellationRequested();
        RandomProvider.Next(2, 5);
    } 
    double b = RandomProvider.Next(2, 5); 
    double c = b /a; 
    double e = RandomProvider.Next(8, 11); 
    while (e == 9) 
    { 
        cancelToken.ThrowIfCancellationRequested();
        RandomProvider.Next(8,11); 
    } 
    double f = b / e;
    return f;
}

这使GetFooAsync有机会了解它应该很快退出的事实,并确保它在放弃鬼魂之前进入稳定状态。

答案 1 :(得分:5)

如果您控制需要关闭的代码,则将逻辑写入其中,使其能够从另一个线程中干净地关闭。

如果您不控制需要关闭的代码,则有两种情况。首先,它是敌对给你并积极抵制你关闭它的企图。这是一个错误的情况。理想情况下,您应该在另一个进程中运行该代码,而不是另一个进程,并销毁该进程。一个充满敌意的线程有很多技巧可以阻止你关闭它。

其次,如果它不是敌对的并且它不能干净地关闭那么它的设计很糟糕,或者它是错误的。修理它。如果你无法修复它,请在另一个进程中运行它,就像它是敌对的一样。

你应该做的最后一件事是中止线程。线程中止是纯粹的邪恶,应该只作为最后的手段。正确的做法是设计代码,以便可以干净,快速地取消代码。

更多想法:

http://blogs.msdn.com/b/ericlippert/archive/2010/02/22/should-i-specify-a-timeout.aspx

或者,您可以使用单线程方法。将工作分解成小块,每个工作块的最后一件事就是将下一块工作排入工作队列。然后你坐在一个循环中,将工作拉出队列并执行它。要提前停止,只需清除队列即可。如果你不需要,不需要采用多线程方法。

答案 2 :(得分:1)

您可以通过简单的方式实现这一目标,而无需使用线程:

int x = 0;
int startTime = Environment.TickCount;
for (int i = 0; i < 1000000; i++)
{
    x++;
    int currentTime = Environment.TickCount;
    if ((currentTime - startTime) > 1000)
    {
        break;
    }
}

如果您愿意,可以将Environment.TickCount替换为DateTimeSystem.Diagnostics.Stopwatch

答案 3 :(得分:0)

您可以使用background worker,此类支持取消

答案 4 :(得分:0)

如果问题是循环中迭代次数多于循环内部的迭代次数,则可以请求保存循环开始时的时间,并查看每次迭代之间的差异(或者可能是%10,20) ,100)并取消,如果您想在该任务上花费的时间结束。