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

本文关键字:取消 请求 处理 时间 如果 | 更新日期: 2023-09-27 17:56:37

你好,这在 c# 中可能吗:

如果我有一个循环,说:

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

并且需要 1 秒多的时间才能完成,是否可以杀死代码并继续前进?

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

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

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 的评论(和其他人)中看到的那样,中止线程并不是很优雅(又名"纯粹的邪恶")。如果在此循环中发生重要事情,则通过在循环中间强制线程流产,最终可能会使数据处于不稳定状态。所以这是可能的,但你是否使用这种方法将取决于你在循环中到底在做什么。

因此,更好的选择是让您的循环在被告知退出时自愿决定退出(更新 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一个机会,让他们意识到它应该很快就会退出,并确保它在放弃幽灵之前进入稳定状态。

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

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

其次,如果它没有敌意并且不能干净地关闭,那么它要么设计糟糕,要么是有缺陷的。 修复它。如果您无法修复它,请在另一个进程中运行它,就像它是敌对的一样。

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

更多想法:

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

或者,您可以使用单线程方法。将工作分解为小块,其中每个工作块所做的最后一件事是将下一个工作块排队到工作队列中。然后你坐在一个循环中,从队列中提取工作并执行它。要提早停止,只需清除队列即可。如果不需要,则无需使用多线程方法。

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

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替换为DateTime呼叫或System.Diagnostics.Stopwatch

您可以使用后台工作者,此类支持取消

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