C# 为Parallel.ForEach中的项设置超时

C# 为Parallel.ForEach中的项设置超时,c#,.net,concurrency,timeout,parallel.foreach,C#,.net,Concurrency,Timeout,Parallel.foreach,我正在使用Parallel.ForEach处理并发字典集合。ConcurrentDictionary包含键和字节字段 concurrentDictionary-这里是并发字典集合 Parallel.ForEach(concurentDictionary, (item) => { var mailObject = dbContext.MailObjects.Where(x=> x.MailObjectId == item.key).Select(y=> y); m

我正在使用Parallel.ForEach处理并发字典集合。ConcurrentDictionary包含键和字节字段

concurrentDictionary-这里是并发字典集合

Parallel.ForEach(concurentDictionary, (item) =>
{
    var mailObject = dbContext.MailObjects.Where(x=> x.MailObjectId == item.key).Select(y=> y);
    mailObject.MailBody = ConvertToPdf(item.Value.MailBody);
} 
dbContext.SaveChanges();

我想设置超时,如果任何特定项目需要设置的最长时间,比如60秒。如果需要60秒以上,我想退出,不保存该特定项目。其余项目应通过
dbContext.SaveChanges()方法保存。如何并行实现这一点。ForEach?

类似这样的东西可能接近您想要的

CancellationTokenSource cts = new CancellationTokenSource();

ParallelOptions parallelOptions = new ParallelOptions();
parallelOptions.CancellationToken = cts.Token;
parallelOptions.MaxDegreeOfParallelism = System.Environment.ProcessorCount;

Timer timer = new Timer(callback => { cts.Cancel(); }, null, 60*1000, Timeout.Infinite);

try
{
    Parallel.ForEach(concurentDictionary, parallelOptions, (item) =>
    {
        var mailObject = dbContext.MailObjects.Where(x => x.MailObjectId == item.key).Select(y => y);
        mailObject.MailBody = ConvertToPdf(item.Value.MailBody);
        parallelOptions.CancellationToken.ThrowIfCancellationRequested();
    });
}
catch (OperationCanceledException e)
{
    // Log the cancellation event...
}
finally
{
    dbContext.SaveChanges();
    cts.Dispose();
}
下面是解决方案的完整修订版,它为每个迭代确定了计时器的范围。如果一次(或多次)超过时间限制,它将利用loopstate停止迭代,而不是取消令牌。这并不依赖于异常来退出循环。然而,在编写时,任何异常也会触发所有循环停止。请注意,这不会立即停止所有循环,并将完成现有循环的执行,直到它们也完成或超时

{
    if (Parallel.ForEach(concurentDictionary, (item, loopState) =>
        {
            Timer timer = new Timer(callback => { loopState.Stop(); }, null, 60 * 1000, Timeout.Infinite);

            try
            {
                if (loopState.ShouldExitCurrentIteration || loopState.IsExceptional)
                    loopState.Stop();

                var mailObject = dbContext.MailObjects.Where(x => x.MailObjectId == item.key).Select(y => y);
                mailObject.MailBody = ConvertToPdf(item.Value.MailBody);

                if (loopState.ShouldExitCurrentIteration || loopState.IsExceptional)
                    loopState.Stop();
            }
            catch (Exception)
            {
                loopState.Stop();
                throw;
            }
            finally
            {
                timer.Dispose();
            }
        }
    ).IsCompleted)
    {
        // All events complete
    }

}
catch (AggregateException)
{
    // Execution will not get to this point until all of the iterations have completed (or one 
    // has failed, and all that were running when that failure occurred complete).
}

像这样的东西可能接近你想要的

CancellationTokenSource cts = new CancellationTokenSource();

ParallelOptions parallelOptions = new ParallelOptions();
parallelOptions.CancellationToken = cts.Token;
parallelOptions.MaxDegreeOfParallelism = System.Environment.ProcessorCount;

Timer timer = new Timer(callback => { cts.Cancel(); }, null, 60*1000, Timeout.Infinite);

try
{
    Parallel.ForEach(concurentDictionary, parallelOptions, (item) =>
    {
        var mailObject = dbContext.MailObjects.Where(x => x.MailObjectId == item.key).Select(y => y);
        mailObject.MailBody = ConvertToPdf(item.Value.MailBody);
        parallelOptions.CancellationToken.ThrowIfCancellationRequested();
    });
}
catch (OperationCanceledException e)
{
    // Log the cancellation event...
}
finally
{
    dbContext.SaveChanges();
    cts.Dispose();
}
下面是解决方案的完整修订版,它为每个迭代确定了计时器的范围。如果一次(或多次)超过时间限制,它将利用loopstate停止迭代,而不是取消令牌。这并不依赖于异常来退出循环。然而,在编写时,任何异常也会触发所有循环停止。请注意,这不会立即停止所有循环,并将完成现有循环的执行,直到它们也完成或超时

{
    if (Parallel.ForEach(concurentDictionary, (item, loopState) =>
        {
            Timer timer = new Timer(callback => { loopState.Stop(); }, null, 60 * 1000, Timeout.Infinite);

            try
            {
                if (loopState.ShouldExitCurrentIteration || loopState.IsExceptional)
                    loopState.Stop();

                var mailObject = dbContext.MailObjects.Where(x => x.MailObjectId == item.key).Select(y => y);
                mailObject.MailBody = ConvertToPdf(item.Value.MailBody);

                if (loopState.ShouldExitCurrentIteration || loopState.IsExceptional)
                    loopState.Stop();
            }
            catch (Exception)
            {
                loopState.Stop();
                throw;
            }
            finally
            {
                timer.Dispose();
            }
        }
    ).IsCompleted)
    {
        // All events complete
    }

}
catch (AggregateException)
{
    // Execution will not get to this point until all of the iterations have completed (or one 
    // has failed, and all that were running when that failure occurred complete).
}

或者当我重读你的问题时,这可能不是。如果任何一项花费的时间超过60秒,听起来好像你想要取消。在这种情况下,您可以为具有相同回调的每个并行循环定义一个新计时器。您指的是parallel.ForEach中的计时器。你能告诉我怎么做吗?谢谢或者当我重读你的问题时,这可能不是。如果任何一项花费的时间超过60秒,听起来好像你想要取消。在这种情况下,您可以为具有相同回调的每个并行循环定义一个新计时器。您指的是parallel.ForEach中的计时器。你能告诉我怎么做吗?谢谢