如何解决将ETA添加到嵌入式循环序列
更新1:
下面提供的一些解决方案似乎不错。但是,我只知道确定父循环的迭代之后,循环将迭代的次数。因此,我无法预先计算所有迭代次数。
原始问题:
我在类似于以下的程序中嵌入了循环:
Prog1:
using System;
using System.Threading;
namespace time_remaining_loop_strip
{
class Program
{
static void Main(string[] args)
{
var random = new Random();
Console.Clear();
// Simulate initiation delay
Console.WriteLine("initiate");
Thread.Sleep(random.Next(100,1000));
int intCount = random.Next(1,10);
for (int loop1 = 0; loop1 <= intCount; loop1++)
{
// Simulate loop1 delay
Console.WriteLine("\tloop1");
Thread.Sleep(random.Next(100,1000));
for (int loop2 = 0; loop2 <= random.Next(1,10); loop2++)
{
// Simulate loop2 delay
Console.WriteLine("\t\tloop2");
Thread.Sleep(random.Next(100,1000));
for (int loop3 = 0; loop3 <= random.Next(1,10); loop3++)
{
// Simulate loop3 delay
Console.WriteLine("\t\t\tloop3");
Thread.Sleep(random.Next(100,1000));
for (int loop4 = 0; loop4 <= random.Next(1,10); loop4++)
{
// Simulate loop4 delay
Console.WriteLine("\t\t\t\tloop4");
Thread.Sleep(random.Next(100,1000));
}
}
}
}
}
}
}
我正在尝试显示“剩余处理时间(ETA)”,因此我可以粗略估算出上述循环序列完成之前剩余的时间量
我现在还有另一段代码,它确实显示了一个ETA,当循环非常简单时,它可以正常工作:
Prog2:
using System;
using System.Threading;
namespace time_remaining
{
class Program
{
public static TimeSpan ComputeRemaining((int count,DateTime time) start,(int count,DateTime time) current,int end) =>
current.count - start.count == 0
? TimeSpan.MaxValue
: TimeSpan.FromSeconds((end - current.count) * current.time.Subtract(start.time).TotalSeconds / (current.count - start.count));
static void Main(string[] args)
{
Console.Clear();
var random = new Random();
int Count = random.Next(10,60);
DateTime startTime = DateTime.Now;
for (int i = 0; i <= Count; i++)
{
Thread.Sleep(random.Next(100,2000));
TimeSpan timeRemaining = ComputeRemaining((0,startTime),(i,DateTime.Now),Count);
Console.SetCursorPosition(0,0);
Console.Write("ETA: ");
Console.Write(String.Format("{0} Days,{1} Hours,{2} Minutes,{3} Seconds",timeRemaining.Days.ToString().PadLeft(3,'0'),timeRemaining.Hours.ToString().PadLeft(2,timeRemaining.Minutes.ToString().PadLeft(2,timeRemaining.Seconds.ToString().PadLeft(2,'0')));
}
}
}
}
当我尝试将Prog1的ETA方面结合到Prog2中时,它似乎不能很好地工作:
Prog3 = Prog1 + Prog2:
using System;
using System.Threading;
namespace time_remaining_loop_strip
{
class Program
{
public static TimeSpan ComputeRemaining((int count,int end) =>
current.count - start.count == 0
? TimeSpan.MaxValue
: TimeSpan.FromSeconds((end - current.count) * current.time.Subtract(start.time).TotalSeconds / (current.count - start.count));
static void Main(string[] args)
{
DateTime startTime = DateTime.Now;
var random = new Random();
Console.Clear();
// Simulate initiation delay
//Console.WriteLine("initiate");
Thread.Sleep(random.Next(100,10);
for (int loop1 = 0; loop1 <= intCount; loop1++)
{
// Simulate loop1 delay
//Console.WriteLine("\tloop1");
Thread.Sleep(random.Next(100,10); loop2++)
{
// Simulate loop2 delay
//Console.WriteLine("\t\tloop2");
Thread.Sleep(random.Next(100,10); loop3++)
{
// Simulate loop3 delay
//Console.WriteLine("\t\t\tloop3");
Thread.Sleep(random.Next(100,10); loop4++)
{
// Simulate loop4 delay
//Console.WriteLine("\t\t\t\tloop4");
Thread.Sleep(random.Next(100,1000));
}
}
}
TimeSpan timeRemaining = ComputeRemaining((0,(loop1,intCount);
Console.SetCursorPosition(0,'0')));
}
}
}
}
这似乎一点也不奏效。它确实显示了ETA,但是由于循环的结构方式,它在显示任何内容之前都有很长的延迟。
如何更新此信息,以便ETA代码更准确,更可预测的间隔(如每秒)显示ETA?
解决方法
基于这样的假设,您已经建立了一个简单的实际发生情况的模型(您有一系列嵌套的变量延迟和计数过程,甚至在运行时都无法确定),因此当前需要的是随机数预测子。
n
和m
持续时间(100和1000ms)之间将有o
和p
个周期(在您的示例中为1和10),但仍然是随机的好吧,随机量化为频段。您编写的方式是骰子随机的(骰子没有记忆),尽管在实践中,一个周期的持续时间似乎更有可能暗示着下一个周期的持续时间(这就是您的做法) ComputeRemaining
),并且在一个频段内,一个循环的计数必须有助于下一循环的计数。
因此,尽管Prog2
看起来很简单,但仍涵盖了我们的示例..给定一个已知的循环计数,其中每个周期需要一个随机的持续时间(实际上是pick(n,m)^3*pick(o,p)
..但这仍然只是随机的数字)-预测结束。为了报告的目的,您也希望重构它以考虑内部循环,但是实际上是相同的过程。 ( ^ 3是一种简化,实际上是一系列独立的选择权相乘)
好吧,所以我们不需要谈论时间/延迟(我的意思是..您显然希望这样做,但这只是代表未来的某个数字-TimeSpan
是long
一段时间以来x滴答的计数...结束时间仅为Now + x*tick
)。因此,我们可以将其简化为long
预测变量。
设置
interface IAvger
{
public double Avg { get; }
}
interface IAdder
{
void Add(long value);
}
class Mean
{
private int _count = 0;
public double Total { get; private set; } = 0;
public double? Avg => _count == 0 ? null : (double?)(Total / _count);
public void Add(double val)
{
Total += val;
_count++;
}
}
您可以忽略接口(我在切换潜在解决方案时使用了它们)。类Mean
应该很熟悉...它计算多个值的平均平均值,并在找到更多值时进行缩放/调整。
/// <summary>
/// Equivalent to your ComputeRemaining
/// </summary>
class RunningAvg : IAvger,IAdder
{
private Mean _mean = new Mean();
private readonly double _guess;
public RunningAvg(double guess)
{
_guess = guess;
}
public double Avg => _mean.Avg ?? _guess;
public void Add(long value) => _mean.Add(value);
}
这里等同于您的ComputeRemaining
。 guess
的值有助于在其他情况一无所知的情况下进行早期预测(与TimeSpan.Max
等效)
/// <summary>
/// Drop the lowest and highest value
/// - Fairly typical in stats,however note this is only one biggest/smallest
/// (will work best when the standard devation is low,and outliers
/// are rare)
/// </summary>
class IgnoreExtremes : IAvger,IAdder
{
private long? _worst;
private long? _best;
private Mean _mean = new Mean();
private readonly int _wt;
private readonly double _guess;
public IgnoreExtremes(double guess,int weight = 4)
{
_wt = weight;
_guess = guess;
}
public long Best => _best ?? (long)Math.Round(_guess);
public long Worst => _worst ?? (long)Math.Round(_guess);
public double Avg
{
get
{
var avg = _mean.Avg;
if (!avg.HasValue) return _guess;
return (Best + _wt * avg.Value + Worst) / (2 + _wt);
}
}
public void Add(long value)
{
if (!_best.HasValue)
{
_best = value;
}
else if (value < _best)
{
_mean.Add(_best.Value);
_best = value;
}
else if (!_worst.HasValue)
{
_worst = value;
}
else if (value > _worst)
{
_mean.Add(_worst.Value);
_worst = value;
}
else
{
_mean.Add(value);
}
}
}
最后一些统计! IgnoreExtremes
抑制最高和最低(单个)值。在科学采样中,通常会忽略它们,但是如果使用数字的真实随机分布(例如骰子掷骰或random.Next
),则只会丢弃一个极端。这应该比RunningAvg
预测更好的数字。请注意,这是加权平均的一种形式,您可以通过在构造时提供weight
值(wt=4
很普遍)或将_wt
与{{1} }(需要更改一些代码)
_mean.count
另一种统计方法是忽略平均值超过class IgnoreStdDevOutlier : IAvger,IAdder
{
private const int AT_LEAST = 5;
private Mean _mean = new Mean();
private readonly List<long> _vals = new List<long>();
private readonly double _guess;
//private long _tot;
private readonly double _outlierStdDevMulti;
public IgnoreStdDevOutlier(double guess,double outlierStdDevMulti = 2)
{
_guess = guess;
_outlierStdDevMulti = outlierStdDevMulti;
}
private double StdDev()
{
var avg = Avg;
double tot = 0;
foreach (var item in _vals)
tot += (item - avg) * (item - avg);
return Math.Sqrt(tot / (_vals.Count - 1));
}
public void Add(long value)
{
_vals.Add(value);
if (_vals.Count > AT_LEAST)
{
var avg = Avg;
var sd = StdDev();
var min = avg - _outlierStdDevMulti * sd;
var max = avg + _outlierStdDevMulti * sd;
//Ignore outliers
if (value < min || value > max) return;
}
_mean.Add(value);
}
public double Avg => _mean.Avg ?? 0;
}
的值,其中n*StandardDeviation
通常为2或3(您会发现意见不一致)。看到的所有值都是标准偏差的一部分,但只有那些不是异常值的值才被视为平均值的一部分。最终像抑制因素一样起作用,防止了估计值的太大波动。
好的,要运行测试,我们需要某种测量类:
n
真实的猜测无法得知最终(class Performance
{
private readonly List<long> _set = new List<long>();
private long _actual;
public void Add(long item) => _set.Add(item);
public void SetFinal(long final) => _actual = final;
public void Report()
{
foreach (var item in _set)
{
Console.WriteLine("{0} {1} = {2}",item,_actual,(item / (double)_actual - 1) * 100);
}
}
}
)的值,但是该类使我们可以看到到目前为止的猜测如何。
最后是程序类:
_actual
您可以忽略工作是通过称为class Program
{
const int MIN_MSEC = 100;
const int MAX_MSEC = 1000;
const int MIN_LOOP = 10;
const int MAX_LOOP = 50;
static void C(Random random)
{
int nm = random.Next(MAX_LOOP,MAX_LOOP);
var guess = (double)((MAX_LOOP + MIN_LOOP) / 2 * (MAX_MSEC + MIN_MSEC) / 2);
var predict = new RunningAvg(guess);
//var predict = new IgnoreExtremes(guess);
//var predict = new IgnoreStdDevOutlier(guess,3);
var per = new Performance();
long tot = 0;
for (int i = 0; i <= nm; i++)
{
var op = random.Next(MIN_MSEC,MAX_MSEC);
predict.Add(op);
per.Add((long)Math.Round(predict.Avg * nm));
tot += op;
}
per.SetFinal(tot);
per.Report();
}
static void Main(string[] args)
{
var random = new Random();
C(random);
}
}
的方法完成的(只是代码的副作用-C
是Prog1,而A
是Prog2)。在B
中,尝试更改C
,RunningAvg
或IgnoreExtremes
中未注释的内容。同样,因为所写内容是骰子随机的,所以您不能将一次运行作为一个良好的基准。下一阶段是将其包装在重复运行中,并取预测的标准偏差的平均值(或者可能仅取较晚的预测-用户可能不介意,如果早期的估计值相差甚远,最终它没有跳来跳去)-但是我没时间了。我发现IgnoreStdDevOutlier
平均会在正确答案上迅速收敛,到最后最终下降了0-1%。 IgnoreStdDevOutlier
只能忽略一个极端(在每个方向上),因此有点像IgnoreExtremes
。如果您的数据不是随机的,并且偶尔只有极端的情况-那就可以了。 IgnoreStdDevOutlier
实际上在某些时候的表现并不理想,而在其他时候则一直以两位数的百分比下降。只要是易于预测的随机数。
使用说明
-
RunningAvg
很长。所有这些都是为了预测很长的时间而写的,可以将其视为当时与现在之间的差异。要直接切换,请使用Timespan.Ticks
来建立跨度,并使用new Timespan(long ticks)
来获得跨度。显然,所有这些类都可以为DateTime.Now.Subtract(startTime).Ticks
而不是TimeSpan
重写。.不幸的是,没有一个简单的通用long
约束同时包含where
和{{1 }}
您可以知道循环4(L4)将执行L1t L2t L3t * L4t =总数的次数。现在,您一开始就声明
dt = DateTime.Now;
count = 0L;
现在在L4内部,您可以增加计数并计算经过的时间
et= DateTime.Now - dt;
count++;
然后使用3的规则计算出要达到总迭代次数的秒数。
remainingSeconds = (total*et.TotalSeconds/count) - et.TotalSeconds;
现在的预计到达时间是
DateTime.Now.AddSeconds(remainingSeconds);
,
我用下面的逻辑来解决这个问题。
-
在每次循环之前,我先计算所有不同的循环计数。
-
之后,我创建了4个不同的变量来保存每个循环的睡眠时间,这些循环是其中的设置。
-
现在在第一个for循环之前,我使用以下逻辑计算了所有循环将花费的总时间(以毫秒为单位):
var totalTime =(firstLoopCount * firstLoopSleepTime)+ (firstLoopCount * secondLoopCount * secondLoopSleepTime)+ (firstLoopCount * secondLoopCount * thirdLoopCount * thirdLoopSleepTime)+ (firstLoopCount * secondLoopCount * thirdLoopCount * thirdLoopCount * FourthLoopSleepTime);
注意:我在函数调用(GetTotoalTimeinMilliSecond)中添加了1,其中循环计数通过了,因为循环从0开始并以不同的循环计数(包括其自身)结束。
- 现在开始循环,打印已花费的总时间
- 在每个循环内,在线程休眠之后,从总时间中减去线程休眠时间并打印出来。并将新的计算时间设置为总时间。
- 在每个循环中重复此操作。
下面是代码:
class Program
{
static void Main(string[] args)
{
DateTime startTime = DateTime.Now;
var random = new Random();
Console.Clear();
// Simulate initiation delay
//Console.WriteLine("initiate");
Thread.Sleep(random.Next(100,1000));
int firstLoopCount = random.Next(1,10);
int secondLoopCount = random.Next(1,10);
int thirdLoopCount = random.Next(1,10);
int fourthLoopCount = random.Next(1,10);
int firstLoopSleepTime = random.Next(100,1000);
int secondLoopSleepTime =random.Next(100,1000);
int thirdLoopSleepTime = random.Next(100,1000);
int fourthLoopSleepTime = random.Next(100,1000);
//**************Added 1 because loop is starting from 0 and ending with different loops count including itself.
var totalTimeinMillSec = GetTotoalTimeinMilliSecond(firstLoopCount + 1,secondLoopCount + 1,thirdLoopCount + 1,fourthLoopCount + 1,firstLoopSleepTime,secondLoopSleepTime,thirdLoopSleepTime,fourthLoopSleepTime);
PrintAndGetTimeRemaining(totalTimeinMillSec);
for (int loop1 = 0; loop1 <= firstLoopCount; loop1++)
{
// Simulate loop1 delay
//Console.WriteLine("\tloop1");
Thread.Sleep(firstLoopSleepTime);
totalTimeinMillSec = PrintAndGetTimeRemaining(totalTimeinMillSec - firstLoopSleepTime);
for (int loop2 = 0; loop2 <= secondLoopCount; loop2++)
{
// Simulate loop2 delay
//Console.WriteLine("\t\tloop2");
Thread.Sleep(secondLoopSleepTime);
totalTimeinMillSec = PrintAndGetTimeRemaining(totalTimeinMillSec - secondLoopSleepTime);
for (int loop3 = 0; loop3 <= thirdLoopCount; loop3++)
{
// Simulate loop3 delay
//Console.WriteLine("\t\t\tloop3");
Thread.Sleep(thirdLoopSleepTime);
totalTimeinMillSec = PrintAndGetTimeRemaining(totalTimeinMillSec - thirdLoopSleepTime);
for (int loop4 = 0; loop4 <= fourthLoopCount; loop4++)
{
// Simulate loop4 delay
//Console.WriteLine("\t\t\t\tloop4");
Thread.Sleep(fourthLoopSleepTime);
totalTimeinMillSec = PrintAndGetTimeRemaining(totalTimeinMillSec - fourthLoopSleepTime);
}
}
}
}
}
private static int PrintAndGetTimeRemaining(int totalTimeinMillSec)
{
TimeSpan timeRemaining = TimeSpan.FromMilliseconds(totalTimeinMillSec);
Console.SetCursorPosition(0,0);
Console.WriteLine("ETA: ");
Console.WriteLine(String.Format("{0} Days,{1} Hours,{2} Minutes,{3} Seconds,{4} Milliseconds",timeRemaining.Days.ToString().PadLeft(3,'0'),timeRemaining.Hours.ToString().PadLeft(2,timeRemaining.Minutes.ToString().PadLeft(2,timeRemaining.Seconds.ToString().PadLeft(2,timeRemaining.Milliseconds.ToString().PadLeft(2,'0')));
return totalTimeinMillSec;
}
private static int GetTotoalTimeinMilliSecond(int firstLoopCount,int secondLoopCount,int thirdLoopCount,int fourthLoopCount,int firstLoopSleepTime,int secondLoopSleepTime,int thirdLoopSleepTime,int fourthLoopSleepTime)
{
var totalTime = (firstLoopCount * firstLoopSleepTime +
firstLoopCount * secondLoopCount * secondLoopSleepTime +
firstLoopCount * secondLoopCount * thirdLoopCount * thirdLoopSleepTime +
firstLoopCount * secondLoopCount * thirdLoopCount * fourthLoopCount * fourthLoopSleepTime);
return totalTime;
}
}
}
如果我错过了什么,请告诉我。
,我认为这可能对您有用;为了完成该解决方案,我创建了一些类来提供帮助。
代码本身有一些注释。
首先是一个枚举,要知道我们在哪个循环中;这不是完全必要的,但是稍后值得重构。
public enum LoopEnum
{
loop1,loop2,loop3,loop4
}
然后我创建了一个名为EtaLoop
的类,它将包含整个循环信息/逻辑,以了解循环的一次迭代需要多长时间,我使用StopWatch
:
public class EtaLoop
{
public readonly int TotalIterations;
private readonly List<long> _loopsTimesInMiliseconds;
private readonly Stopwatch _stopwatch;
public EtaLoop(int totalIterations)
{
//+1 as the loops starts on 0
TotalIterations = totalIterations+1;
_loopsTimesInMiliseconds = new List<long>();
_stopwatch = new Stopwatch();
}
public double AvgExecution()
{
return _loopsTimesInMiliseconds.Any() ? _loopsTimesInMiliseconds.Average(a => a) : 0;
}
public void Start()
{
if(!_stopwatch.IsRunning)
_stopwatch.Start();
}
public void Stop()
{
_stopwatch.Stop();
_loopsTimesInMiliseconds.Add(_stopwatch.ElapsedMilliseconds);
ResetStopWatch();
}
public int CurrentIteration()
{
return _loopsTimesInMiliseconds.Count();
}
public long EstimatedCurrentIteration()
{
return Convert.ToInt64(_loopsTimesInMiliseconds.Average(a => a) * TotalIterations);
}
private void ResetStopWatch()
{
_stopwatch.Reset();
}
}
我认为这些方法足够清晰,无需更多说明。
另一个名为EtaTime
的类将包含更多的逻辑,例如,该类是EtaLoop
的包装器,如果a for具有5次迭代,则它将在列表中包含5个元素。
每个元素(EtaLoop
)都会添加到列表中,一旦我们完成了(所有迭代都完成了),它就会有更多方法,其中一种是“硬方法”,它会得到解释
public class EtaTime
{
public readonly List<EtaLoop> Loops;
public readonly LoopEnum Loop;
private EtaLoop _currentLoop;
public EtaTime(LoopEnum loop)
{
Loops = new List<EtaLoop>();
Loop = loop;
}
public void SetUpTotal(int totalIterations)
{
_currentLoop = new EtaLoop(totalIterations);
}
public void StartLoop()
{
_currentLoop.Start();
}
public void EndLoop()
{
_currentLoop.Stop();
}
public void RegisterLoop()
{
Loops.Add(_currentLoop);
}
/// <summary>
/// Get the average time per execution,and the average number of loops per parent loop.
/// The idea is to know how many times (and how long) the loop x is executed per each x-1
/// </summary>
/// <returns></returns>
public (double,double) GetAvgTimeAndAvgExTimes()
{
double avgTimePerLoop = Loops.Any() ? Loops.Average(a => a.AvgExecution()) : _currentLoop.AvgExecution();
double avgTotalLoopsIteration = Loops.Any() ? Loops.Average(a => a.TotalIterations) : _currentLoop.TotalIterations;
return (avgTimePerLoop,avgTotalLoopsIteration);
}
public int GetCurrentIteration()
{
return _currentLoop.CurrentIteration();
}
public int TotalIterations()
{
return _currentLoop.TotalIterations;
}
}
最后,EtaTimeHelper
的包装器将包含所有EtaTimes
;最初我想列出一个列表,这就是enum
为何使用的原因,但是我认为这样更清楚。
注意:此类可以拆分/移动到扩展方法。
这里的要点是RegisterLoop
,每次完成循环时,我们都必须调用该方法。它必须在这里,因为我需要其他循环的信息。
public class EtaTimeHelper
{
//This part can be done in a list,but i think it is easier to see like this.
public readonly EtaTime Loop1;
public readonly EtaTime Loop2;
public readonly EtaTime Loop3;
public readonly EtaTime Loop4;
public readonly DateTime StartTime;
private DateTime _lastPrintTime;
private const int TimeBetweenPrintsInSeconds = 10;
public EtaTimeHelper()
{
Loop1 = new EtaTime(LoopEnum.loop1);
Loop2 = new EtaTime(LoopEnum.loop2);
Loop3 = new EtaTime(LoopEnum.loop3);
Loop4 = new EtaTime(LoopEnum.loop4);
StartTime = DateTime.Now;
_lastPrintTime = DateTime.MinValue;
}
public void RegisterLoop(LoopEnum loopNumber)
{
switch (loopNumber)
{
case LoopEnum.loop1:
Loop1.RegisterLoop();
break;
case LoopEnum.loop2:
Loop2.RegisterLoop();
break;
case LoopEnum.loop3:
Loop3.RegisterLoop();
break;
case LoopEnum.loop4:
Loop4.RegisterLoop();
break;
default:
throw new NotImplementedException("please register the loop");
}
PrintCompletionTime(DateTime.Now,loopNumber);
}
public void PrintCompletionTime(DateTime printTime,LoopEnum loopNumber)
{
if(_lastPrintTime.AddSeconds(TimeBetweenPrintsInSeconds) < printTime)
{
var time = CalculatePredictionTime(loopNumber);
Print(time);
_lastPrintTime = printTime;
}
}
private long CalculatePredictionTime(LoopEnum loopNumber)
{
switch (loopNumber)
{
case LoopEnum.loop1:
return LoopPrediction(Loop1.GetAvgTimeAndAvgExTimes());
case LoopEnum.loop2:
return Loop2Prediction(Loop1,Loop2);
case LoopEnum.loop3:
return Loop3Prediction(Loop1,Loop2,Loop3);
case LoopEnum.loop4:
return Loop4Prediction(Loop1,Loop3,Loop4);
default:
throw new NotImplementedException("please register the loop");
}
//If all loops in #1 are finished,all sub loops are also finished. which means,it is the "end of the loop"
long LoopPrediction((double,double) avgTimeAndAvgExTimes)
{
double avgTimePerLoop = avgTimeAndAvgExTimes.Item1;
double avgIterations = avgTimeAndAvgExTimes.Item2;
return Convert.ToInt64(avgTimePerLoop * avgIterations);
}
long Loop2Prediction(EtaTime loop1,EtaTime loop2)
{
var loop1Prediction = LoopPrediction(loop1.GetAvgTimeAndAvgExTimes());
var loop2Values = loop2.GetAvgTimeAndAvgExTimes();
long avgPerLoop = LoopPrediction(loop2Values);
var loop1AvgIterations = loop1.GetAvgTimeAndAvgExTimes().Item2;
var expectedLoop2Iterations = loop1AvgIterations;
double loop2Predictions = avgPerLoop * expectedLoop2Iterations;
if (loop1Prediction == 0)
{
return Convert.ToInt64(loop2Predictions);
}
else
{
//1+current iteration
return loop1Prediction + loop2.GetCurrentIteration();
}
}
long Loop3Prediction(EtaTime loop1,EtaTime loop2,EtaTime loop3)
{
var loop1_2Prediction = Loop2Prediction(loop1,loop2);
var loop3Values = loop3.GetAvgTimeAndAvgExTimes();
long avgPerLoop = LoopPrediction(loop3Values);
var loop2AvgIterations = loop2.GetAvgTimeAndAvgExTimes().Item2;
var loop1AvgIterations = loop1.GetAvgTimeAndAvgExTimes().Item2;
var expectedLoop3Iterations = loop2AvgIterations * loop1AvgIterations;
double loop3Predictions = avgPerLoop * expectedLoop3Iterations;
if (loop1_2Prediction == 0)
{
return Convert.ToInt64(loop3Predictions);
}
else
{
//1-2+current iteration
return loop1_2Prediction+ loop3.GetCurrentIteration();
}
}
long Loop4Prediction(EtaTime loop1,EtaTime loop3,EtaTime loop4)
{
var loop1_2_3Prediction = Loop3Prediction(loop1,loop3);
var loop4Values = loop4.GetAvgTimeAndAvgExTimes();
long avgPerLoop = LoopPrediction(loop4Values);
var loop2AvgIterations = loop2.GetAvgTimeAndAvgExTimes().Item2;
var loop1AvgIterations = loop1.GetAvgTimeAndAvgExTimes().Item2;
var loop3AvgIterations = loop3.GetAvgTimeAndAvgExTimes().Item2;
var expectedLoop4Iterations = loop2AvgIterations * loop1AvgIterations* loop3AvgIterations;
double loop4Predictions = avgPerLoop * expectedLoop4Iterations;
if (loop1_2_3Prediction == 0)
{
return Convert.ToInt64(loop4Predictions);
}
else
{
//1-2-3+current iteration
return loop1_2_3Prediction + loop4.GetCurrentIteration();
}
}
}
private void Print(long ms)
{
DateTime estimatedCompletionTime = StartTime.AddMilliseconds(ms);
TimeSpan leftTime = (estimatedCompletionTime - DateTime.Now);
Console.WriteLine("ETA: ");
Console.WriteLine($"{leftTime.Days} Days,{leftTime.Hours} Hours,{leftTime.Minutes} Minutes,{leftTime.Seconds} Seconds");//,leftTime.Days.ToString().PadLeft(3,leftTime.Hours.ToString().PadLeft(2,leftTime.Minutes.ToString().PadLeft(2,leftTime.Seconds.ToString().PadLeft(2,'0')));
Console.WriteLine($"on {estimatedCompletionTime.ToString("yyyy/MM/dd HH:mm:ss")}");
Console.WriteLine($"Current Time: {DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss")}");
}
}
和打印。正如您提到的,您希望打印一些mecanisim,但是您没有指定它是Web的consol应用程序还是winforms应用程序。如果它是桌面应用程序,则可能是winforms,您可能可以执行类似myLabel.SetPropertyThreadSafe
的操作来修改要更新的标签。
我计算了CalculatePredictionTime
上的时间的算法,我试图将其设为n个循环的单个方法,但是我无法。
现在是主体。
我在Thread.Sleep
上做了一些更改,因为在进行测试时,我更容易弄清楚是否有意义。另外,我认为范围是10-100(10x),很大,出于测试原因,我将其减小了。
代码遵循一种模式,首先您需要实例化EtaTimeHelper
类。
- 在每次循环之前,您必须先
setUpTotal
,这意味着您必须通过迭代次数。 - 循环的第一行将是
.StartLoop()
,它将开始StopWatch
,循环的最后一行(在}
之前)将是.EndLoop()
; - 在右括号
}
之后,您必须指明etaTimeHelper.RegisterLoop(loopNumber);
作为参数传递循环号,该循环号将在必要时打印。
static void Main(string[] args)
{
var random = new Random();
Console.Clear();
// Simulate initiation delay
//Console.WriteLine("initiate");
EtaTimeHelper etaTimeHelper = new EtaTimeHelper();
int intCount = random.Next(1,10);
etaTimeHelper.Loop1.SetUpTotal(intCount);
for (int loop1 = 0; loop1 <= intCount; loop1++)
{
etaTimeHelper.Loop1.StartLoop();
// Simulate loop1 delay
Console.WriteLine("\tloop1");
Thread.Sleep(random.Next(40,50));
//number of times the loop 2 is going to execute inside this loop 1;
int loop2times = random.Next(1,10);
etaTimeHelper.Loop2.SetUpTotal(loop2times);
for (int loop2 = 0; loop2 <= loop2times; loop2++)
{
etaTimeHelper.Loop2.StartLoop();
// Simulate loop2 delay
//Console.WriteLine("\t\tloop2");
Thread.Sleep(random.Next(30,40));
//number of times the loop 3 is going to execute inside this loop 3;
int loop3times = random.Next(1,10);
etaTimeHelper.Loop3.SetUpTotal(loop3times);
for (int loop3 = 0; loop3 <= loop3times; loop3++)
{
etaTimeHelper.Loop3.StartLoop();
// Simulate loop3 delay
//Console.WriteLine("\t\t\tloop3");
Thread.Sleep(random.Next(10,20));
var loop4Times = random.Next(1,10);
etaTimeHelper.Loop4.SetUpTotal(loop4Times);
for (int loop4 = 0; loop4 <= loop4Times; loop4++)
{
etaTimeHelper.Loop4.StartLoop();
// Simulate loop4 delay
//Console.WriteLine("\t\t\t\tloop4");
Thread.Sleep(random.Next(20,30));
etaTimeHelper.Loop4.EndLoop();
}
etaTimeHelper.RegisterLoop(LoopEnum.loop4);
etaTimeHelper.Loop3.EndLoop();
}
etaTimeHelper.RegisterLoop(LoopEnum.loop3);
etaTimeHelper.Loop2.EndLoop();
}
etaTimeHelper.RegisterLoop(LoopEnum.loop2);
etaTimeHelper.Loop1.EndLoop();
}
etaTimeHelper.RegisterLoop(LoopEnum.loop1);
}
这是一个“工作中的”小提琴https://dotnetfiddle.net/Z06W4g 注意:几秒钟后超时,在您的本地计算机上可以正常工作。
,为了每秒确定一次ETA,我所做的就是将事件附加到
class HymnRepository(private val hymnDatabaseDao: HymnDatabaseDao) {
val allhymns: LiveData<List<Data>> = hymnDatabaseDao.getAllHymns()
suspend fun insert(data: Data){
insert(data)
}
suspend fun getHymnById(hymnId:Int):Data{
return getHymnById(hymnId)
}
}
对象上。该事件将每秒触发一次,以便不断向用户提供ETA反馈。
System.Timers.Timer
在您的示例中,我们使用 System.Timers.Timer aTimer = new System.Timers.Timer(1000);
aTimer.Elapsed += (sender,e) => ATimer_Elapsed(sender,e,new MyEventArguments
{
Loop1TotalIterations = intCount,CurrentIndex = loop1,Start = startTime
});
aTimer.AutoReset = true;
aTimer.Enabled = true;
来模拟延迟。
使用Thread.Sleep
将使所有内容进入睡眠状态,因此时间逻辑将不准确。
我所做的是创建一个多线程应用程序-为了在一个线程上运行逻辑而在另一个线程上运行时间。因此,当我们暂停时间检查时,ETA将继续运行。
将所有内容放在一起,我们有以下内容:
Thread.Sleep
我在控制台反馈中添加了一些颜色-使更改更加明显。
,我认为您必须针对所需的操作对其进行几次基准测试,然后进行推断。这里有变量。目标的计算能力和存储类型(如果涉及)和网络(如果涉及)都将对ETA有所贡献,并且每次执行都会有不同的执行时间。但是在进行一些基准测试后,ETA可以被密切预测。而且,如果您想变得真正聪明,则可以显示一条警告,因为x,y和z等原因,它可能比显示的ETA花费更多的时间。目标上运行的其他进程的CPU利用率。
Jon Skeet是这个主题的权威,他在这里拥有丰富的资源: link
也请阅读此处,了解您的操作类型和CPU位数将如何影响您的ETA: link
,这可能不是理想的解决方案,但有可能解决该问题。
首先,您在Tree
中构建一个Action
,其中包含要执行的内容,然后通过迭代树来执行Actions
。
该树充当要执行的内容的索引。即使您可以跟踪正在执行的操作。完成了什么,有什么要完成。
出于演示目的,我已经将动作嵌套在树中的单个级别中,但是您可以在循环等内部尽可能多地嵌套树(前面是伪代码,但poc可以很好地构建和执行)
class Program
{
static void Main(string[] args)
{
var random = new Random();
TreeNode<Action> root = new TreeNode<Action>(() => { });
var loop1 = root.AddChild(() =>
{
int Count = random.Next(1,3);
for (int i = 0; i <= Count; i++)
Thread.Sleep(random.Next(100,2000));
});
var loop2 = loop1.AddChild(() =>
{
int Count = random.Next(1,2000));
});
var loop3 = loop2.AddChild(() =>
{
int Count = random.Next(1,2000));
});
var loop4 = loop3.AddChild(() =>
{
int Count = random.Next(1,2000));
});
var loop5 = loop4.AddChild(() =>
{
int Count = random.Next(1,2000));
});
var loop6 = loop5.AddChild(() =>
{
int Count = random.Next(1,2000));
});
root.Execute(DateTime.Now);
Console.ReadLine();
}
}
public static class Extensions
{
public static string Humanize(this TimeSpan timeRemaining)
{
return String.Format("{0} Days,{3} Seconds",'0'));
}
public static void PrintETA(this TimeSpan timeRemaining)
{
//Console.SetCursorPosition(0,0);
Console.Write("ETA: ");
Console.Write(timeRemaining.Humanize());
}
public static TimeSpan ComputeRemaining((int count,DateTime time) start,(int count,DateTime time) current,int end) =>
current.count - start.count == 0
? TimeSpan.MaxValue
: TimeSpan.FromSeconds((end - current.count) * current.time.Subtract(start.time).TotalSeconds / (current.count - start.count));
public static void Execute(this TreeNode<Action> root,DateTime startTime)
{
var current = root;
var end = current.Count();
var currentCount = 1;
Iterate:
var Count = current.Children.Count();
for (int i = 0; i < Count; i++)
{
TreeNode<Action> node = current.Children.ElementAt(i);
node.Data();
TimeSpan timeRemaining = ComputeRemaining((0,startTime),(currentCount++,DateTime.Now),end-1);
timeRemaining.PrintETA();
Console.WriteLine();
Console.WriteLine("Processed {0} of {1}",currentCount - 1,end-1);
if (node.Children.Count() > 0)
{
current = node;
goto Iterate;
}
}
}
}
参考:
TreeNode.cs
另一个树结构:https://github.com/gt4dev/yet-another-tree-structure
已更新。 答案的第一个版本在每次迭代结束时计算RemainingTime和TotalTime的值。给定for循环的嵌套方式,这可能导致更新之间的长时间延迟。为了以给定的时间间隔读取这些值,需要进行一些更改。
让我们从Loop类开始。它用于跟踪每个for循环的详细信息,例如迭代总数,当前迭代以及每次迭代消耗的时间。为了完善后者,使用了两个System.Diagnostic秒表。保持自由运行,而无需复位,以简化迭代平均时间的计算。通过属性获取器访问时,每次迭代都会重置另一个时钟,以提供LoopTime的值,该值可用于RemainingTime和TotalTime的即时计算中。当迭代结束时,通过StopClock()方法发出信号,将更新平均循环时间和相关属性。此处获得的值比即时计算出的值更准确。
它的父类LoopTimer类负责创建和存储Loop实例的引用,启动和停止时钟以及计算全局ETA。在for循环的开始使用EnterLoop()方法。它创建一个新的循环。在其余的迭代中,将使用具有单个参数的重载EnterLoop()来检索存储在Dictionary中的Loop实例。循环结束时,方法ExitLoop()停止时钟并更新计算。
提供函数ExitLoopRetETA()和ExitLoopRetTotalEta()作为ExitLoop()的替代品,以在循环结束时打印数据,以进行测试或调试。方法Bonus()显示了如何使用它。方法ShowStatus()和函数GetStatus提供有关Loop对象的内部信息。
要定期显示这些值,可以使用一个Task来在单独的线程中运行DoUpdate()(或DoUpdateTotal()或DoUpdateStatus())。
目标框架是.Net 4.0
工人阶级:
using System;
using System.Collections.Generic;
using System.Diagnostics;
namespace LoopTimer
{
public struct TotalEta
{
public TimeSpan Eta;
public TimeSpan Total;
}
internal class LoopTimer
{
// Private helper class
private class Loop
{
// Declarations
private Stopwatch _clock;
private Stopwatch _interval_clock;
// Constructor
public Loop(int index,int count)
{
Index = index;
Count = count;
_clock = new Stopwatch();
_interval_clock = new Stopwatch();
}
// Properties
internal int Index { get; set; }
internal int Count { get; private set; }
private double _loopTimeMs;
internal double LoopTimeMs
{
get
{
if (_clock.IsRunning)
{
return _interval_clock.Elapsed.TotalMilliseconds;
}
else
return _loopTimeMs; ;
}
}
private double _remainingTimeMs;
internal double RemainingTimeMs
{
get
{
if (_clock.IsRunning)
return CalculateRemainingTime();
else
return _remainingTimeMs;
}
}
private double _totalTimeMs;
internal double TotalTimeMs
{
get
{
if (_clock.IsRunning)
return CalculateTotalTime();
else
return _totalTimeMs;
}
}
internal TimeSpan LoopTime
{
get { return TimeSpan.FromMilliseconds(LoopTimeMs); }
}
internal TimeSpan TotalTime
{
get { return TimeSpan.FromMilliseconds(TotalTimeMs); }
}
internal TimeSpan RemainingTime
{
get { return TimeSpan.FromMilliseconds(RemainingTimeMs); }
}
// Methods
internal void StartClock()
{
_clock.Start();
_interval_clock.Start();
}
internal void StopClock()
{
_clock.Stop();
_interval_clock.Stop();
UpdateTimes();
_interval_clock.Reset();
}
private void UpdateTimes()
{
// reading clock
double elapsed = _clock.Elapsed.TotalMilliseconds;
// Calculating average loop time. The Stopwatch is not reseted between iterations.
_loopTimeMs = elapsed / (Index + 1);
// Calculating estimated remaining time = average loop time * remaining iterations.
_remainingTimeMs = CalculateRemainingTime();
// Calculating estimated total time = average loop time * iterations.
_totalTimeMs = CalculateTotalTime();
}
private double CalculateRemainingTime()
{
// Calculating estimated remaining time = average loop time * remaining iterations.
double time;
int countt = Count - Index;
if (countt > 1)
time = LoopTimeMs * countt;
else if (countt == 1)
time = LoopTimeMs;
else
time = 0;
return time;
}
private double CalculateTotalTime()
{
return LoopTimeMs * Count;
}
}
// End Private helper class
// Declarations
private Dictionary<int,Loop> _loopDict;
private int _loopIndex;
// Constructor
public LoopTimer()
{
_loopDict = new Dictionary<int,Loop>();
_loopIndex = -1;
}
// Properties
internal TimeSpan TotalTime
{
get { return TimeSpan.FromMilliseconds(TotalTimeMs); }
}
internal TimeSpan RemainingTime
{
get { return TimeSpan.FromMilliseconds(RemainingTimeMs); }
}
private double TotalTimeMs
{ get { return CalculateTotalTime(); } }
private double RemainingTimeMs
{ get { return CalculateRemainingTime(); } }
// Methods
internal void EnterLoop(int index,int count)
{
Loop newLoop;
// increase index
_loopIndex++;
if (!_loopDict.ContainsKey(_loopIndex))
{
// create new Loop
newLoop = new Loop(index,count);
_loopDict[_loopIndex] = newLoop;
}
else
{ // retrieve Loop from Dict
newLoop = _loopDict[_loopIndex];
}
newLoop.StartClock();
}
internal void EnterLoop(int index)
{
// increase index
_loopIndex++;
// retrive loop & start clock
_loopDict[_loopIndex].Index = index;
_loopDict[_loopIndex].StartClock();
}
internal void ExitLoop()
{ // retrive loop & stop clock
_loopDict[_loopIndex].StopClock();
// decrease index
_loopIndex--;
}
// bonus method
internal TimeSpan ExitLoopRetETA()
{ // retrive loop & stop clock
_loopDict[_loopIndex].StopClock();
// decrease index
_loopIndex--;
return RemainingTime;
}
// bonus method
internal TotalEta ExitLoopRetTotalEta()
{
TotalEta retval;
retval.Eta = ExitLoopRetETA();
retval.Total = TotalTime;
return retval;
}
// debug method
internal void ShowStatus()
{
Console.WriteLine("Status:");
Console.WriteLine(" RemainingTime:");
for (int i = 0; i < _loopDict.Count; i++)
{
TimeSpan time = _loopDict[i].RemainingTime;
Console.WriteLine(string.Format(" Loop: {0} Value: {1}",i,time.ToString()));
}
Console.WriteLine();
}
// debug method
internal TotalEta[] GetStatus()
{
TotalEta[] retArr = new TotalEta[_loopDict.Count];
TotalEta retval;
for (int i = 0; i < _loopDict.Count; i++)
{
retval = new TotalEta();
retval.Eta = _loopDict[i].RemainingTime;
retval.Total = _loopDict[i].TotalTime;
retArr[i] = retval;
}
return retArr;
}
private double CalculateRemainingTime()
{
double max,time;
max = 0;
// Remaining Time,the greater of all
for (int i = 0; i < _loopDict.Count; i++)
{
time = _loopDict[i].RemainingTimeMs;
if (time > max)
max = time;
}
return max;
}
// Total Time,bonus
private double CalculateTotalTime()
{
double max,time;
max = 0;
// Total Time,the greater of all
for (int i = 0; i < _loopDict.Count; i++)
{
time = _loopDict[i].TotalTimeMs;
if (time > max)
max = time;
}
return max;
}
}
}
示例程序:
using System;
using System.Threading;
using System.Threading.Tasks;
namespace LoopTimer
{
class Solution
{
static CancellationTokenSource ts;
static void Main(string[] args)
{
Console.Clear();
LoopTimer lm = new LoopTimer();
var random = new Random();
// For easy change test parameters
int minRndCount = 1;
int maxRndCount = 10;
int minRndSleep = 100;
int maxRndSleep = 1000;
// A task to update console,with cancellation token
ts = new CancellationTokenSource();
Task updater = new Task(() => DoUpdate(lm),ts.Token);
// Uncomment to show estimated total time.
//Task updater = new Task(() => DoUpdateTotal(lm),ts.Token);
// Uncomment to show estimated total time and internal values of every loop.
//Task updater = new Task(() => DoUpdateStatus(lm),ts.Token);
// Simulate initiation delay
Thread.Sleep(random.Next(minRndSleep,maxRndSleep));
// Console.WriteLine("initiate");
updater.Start();
int intCountL1 = random.Next(minRndCount,maxRndCount);
for (int loop1 = 0; loop1 <= intCountL1; loop1++)
{
// Entering loop1
if (loop1 == 0)
lm.EnterLoop(loop1,intCountL1);
else
lm.EnterLoop(loop1);
// Simulate loop1 delay
//Console.WriteLine("\tloop1");
Thread.Sleep(random.Next(minRndSleep,maxRndSleep));
int intCountL2 = random.Next(minRndCount,maxRndCount);
for (int loop2 = 0; loop2 <= intCountL2; loop2++)
{
// Entering loop2
if (loop2 == 0)
lm.EnterLoop(loop2,intCountL2);
else
lm.EnterLoop(loop2);
// Simulate loop2 delay
//Console.WriteLine("\t\tloop2");
Thread.Sleep(random.Next(minRndSleep,maxRndSleep));
int intCountL3 = random.Next(minRndCount,maxRndCount);
for (int loop3 = 0; loop3 <= intCountL3; loop3++)
{
// Entering loop3
if (loop3 == 0)
lm.EnterLoop(loop3,intCountL3);
else
lm.EnterLoop(loop3);
// Simulate loop3 delay
//Console.WriteLine("\t\t\tloop3");
Thread.Sleep(random.Next(minRndSleep,maxRndSleep));
int intCountL4 = random.Next(minRndCount,maxRndCount);
for (int loop4 = 0; loop4 <= intCountL4; loop4++)
{
// Entering loop4
if (loop4 == 0)
lm.EnterLoop(loop4,intCountL4);
else
lm.EnterLoop(loop4);
// Simulate loop4 delay
//Console.WriteLine("\t\t\t\tloop4");
Thread.Sleep(random.Next(minRndSleep,maxRndSleep));
// Exiting loop4
lm.ExitLoop();
}
// Exiting loop3
lm.ExitLoop();
}
// Exiting loop2
lm.ExitLoop();
}
// Exiting loop1
lm.ExitLoop();
}
ts.Cancel();
}
static private void DoUpdate(LoopTimer lm)
{
char[] animchar = { '|','/','-','\\' };
int index = 0;
Thread.Sleep(100);
while (true)
{
TimeSpan eta = lm.RemainingTime;
Console.SetCursorPosition(0,0);
Console.Write(string.Format(" {4} ETA: {0} Days,eta.Days.ToString().PadLeft(3,eta.Hours.ToString().PadLeft(2,eta.Minutes.ToString().PadLeft(2,eta.Seconds.ToString().PadLeft(2,animchar[index].ToString()));
if (++index > 3)
index = 0;
Thread.Sleep(1000);
ts.Token.ThrowIfCancellationRequested();
}
}
/*
This method is provided as a sample on displaying the estimated total time.
*/
static private void DoUpdateTotal(LoopTimer lm)
{
char[] animchar = { '|','\\' };
int index = 0;
Thread.Sleep(100);
while (true)
{
TimeSpan eta = lm.RemainingTime;
TimeSpan total = lm.TotalTime;
Console.SetCursorPosition(0,animchar[index].ToString()));
Console.Write(string.Format("\n Total: {0} Days,total.Days.ToString().PadLeft(3,total.Hours.ToString().PadLeft(2,total.Minutes.ToString().PadLeft(2,total.Seconds.ToString().PadLeft(2,'0')));
if (++index > 3)
index = 0;
Thread.Sleep(1000);
ts.Token.ThrowIfCancellationRequested();
}
}
/*
This method is provided as a sample on displaying the estimated total time,and
the internal values of every loop.
*/
static private void DoUpdateStatus(LoopTimer lm)
{
char[] animchar = { '|','\\' };
int index = 0;
Thread.Sleep(100);
while (true)
{
TimeSpan eta = lm.RemainingTime;
TimeSpan total = lm.TotalTime;
TotalEta[] status = lm.GetStatus();
Console.SetCursorPosition(0,'0')));
Console.WriteLine();
int loop = 0;
foreach (var item in status)
{
Console.Write(string.Format("\n Loop: {0} ETA: {1} \tTotal: {2}",loop,item.Eta.ToString(@"hh\:mm\:ss\.FFFF"),item.Total.ToString(@"hh\:mm\:ss\.FFFF")));
loop++;
}
if (++index > 3)
index = 0;
Thread.Sleep(1000);
ts.Token.ThrowIfCancellationRequested();
}
}
/*
This method is provided as a sample for variations on
the ExitLoopRet method. Uses in-place calls.
*/
static internal void Bonus()
{
TotalEta remVal;
TimeSpan remTime;
LoopTimer lm = new LoopTimer();
Console.Clear();
// easy change test parameters
var random = new Random();
int minRndCount = 1;
int maxRndCount = 5;
int maxRndSleep = 1000;
// First,outer loop
int intCountL1 = random.Next(minRndCount,maxRndCount);
for (int i = 0; i < intCountL1; i++)
{
if (i == 0)
lm.EnterLoop(i,intCountL1);
else
lm.EnterLoop(i);
Console.WriteLine(string.Format("\nLoop1 begin iteration: {0} of {1}. Will work(sleep) for {2} ms.",intCountL1 - 1,maxRndSleep));
Thread.Sleep(maxRndSleep);
// Second,middle loop
int intCountL2 = random.Next(minRndCount,maxRndCount);
for (int j = 0; j < intCountL2; j++)
{
if (j == 0)
lm.EnterLoop(j,intCountL2);
else
lm.EnterLoop(j);
Console.WriteLine(string.Format("\n\tLoop2 begin iteration: {0} of {1}. Will work(sleep) for {2} ms.",j,intCountL2 - 1,maxRndSleep));
Thread.Sleep(maxRndSleep);
// Third,inner loop
int intCountL3 = random.Next(minRndCount,maxRndCount);
for (int k = 0; k < intCountL3; k++)
{
if (k == 0)
lm.EnterLoop(k,intCountL3);
else
lm.EnterLoop(k);
Console.WriteLine(string.Format("\n\t\tLoop3 begin iteration: {0} of {1}. Will work(sleep) for {2} ms.",k,intCountL3 - 1,maxRndSleep));
Thread.Sleep(maxRndSleep);
lm.ExitLoop();
Console.WriteLine(string.Format("\n\t\tLoop3 end iteration: {0} of {1}",intCountL3 - 1));
lm.ShowStatus();
}
remTime = lm.ExitLoopRetETA();
Console.WriteLine(string.Format("\n\tLoop2 end iteration: {0} of {1}",intCountL2 - 1));
Console.WriteLine("\t\tRem: " + remTime.ToString());
}
remVal = lm.ExitLoopRetTotalEta();
Console.WriteLine(string.Format("\nLoop1 end iteration: {0} of {1}",intCountL1 - 1));
Console.WriteLine("\t\tTot: " + remVal.Total.ToString());
Console.WriteLine("\t\tRem: " + remVal.Eta.ToString());
}
}
}
}
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。