C#固定时间间隔触发的计数器

发布时间 2023-10-16 09:36:22作者: bsmith

在运行需要很长时间完成的任务时,一般需要定期展示当前处理进度,比如批量文件复制时,显示复制的文件数和总传输字节数,这时候就需要用计数器对处理任务的完成度进行统计,并以一定时间间隔(如500ms)显示当前统计结果。

实现上述功能需要使用一些内部变量来保存当前进度:

        long lastSize;
        long spanSize;
        long spanTime;
        long beginTime;
        long lastTime;

实现add方法,每次内部任务有进度变化时,调用add方法将更新进度保存到内部变量:

        public void add(int count, long size)
        {
            FinishCount += count;
            FinishSize += size;

            long now = DateTime.UtcNow.Ticks;
            if (now - lastTime > ExpireTime)
            {
                spanSize = FinishSize - lastSize;
                spanTime = now - lastTime;

                lastTime = now;
                lastSize = FinishSize;

                trigger();
            }
        }

一般任务处理线程会非常以非常短的时间间隔(如1ms)更新一次进度,所以add内部不能每次触发外部更新,否则会造成性能问题,所以需要先检查上次外部更新的时间,如果时间间隔超过预先设定的间隔,则触发外部更新,外部更新通知通过TimeIsUp事件实现:

        public event Action<Counter> TimeIsUp;

计数器初始化时,可以设定一些初始参数:

        public long ExpireTime = 300 * TimeEx.TicksPerMS;
        public int TotalCount;
        public int FinishCount;
        public long TotalSize;
        public long FinishSize;

如ExpireTime表示外部通知时间间隔,单位为毫秒,TotalCount为子任务数,如果需要统计子任务完成百分比则需要设置此参数,TotalSize为总字节数,如果需要统计任务完成字节数总百分比,则需要设置此参数。

计数器预定了很多统计数据,可以通过公共属性来获取:

        public string Speed => $"{SpeedValue.byteSize(2)}/S";
        public string AvgSpeed => $"{AvgSpeedValue.byteSize(2)}/S";
        public string Duration => (DateTime.UtcNow - new DateTime(beginTime)).ToString(@"hh\:mm\:ss\.ff");
        public string SizePair => $"{FinishSize.byteSize(2)}/{TotalSize.byteSize(2)}";
        public string CountPair => $"{FinishCount}/{TotalCount}";
        public string SizePercent => $"{SizePercentValue}%";
        public string CountPercent => $"{CountPercentValue}%";

        public string CountInfo => $"{CountPair}, {CountPercent}";
        public string FullInfo => $"{SizePercent}, {AvgSpeed}, {CountPair}, {SizePair}, {Duration}";
        public string ShortInfo => $"{SizePair}, {CountPair}, {CountPercent}";
        public string SpeedInfo => $"{Speed}, {SizePair}, {SizePercent}";

也可以根据需要自定义统计数据。

完整代码:Counter.cs

using System;
using util.ext;

namespace util
{
    public class Counter
    {
        public event Action<Counter> TimeIsUp;

        public long ExpireTime = 300 * TimeEx.TicksPerMS;
        public int TotalCount;
        public int FinishCount;
        public long TotalSize;
        public long FinishSize;

        long lastSize;
        long spanSize;
        long spanTime;
        long beginTime;
        long lastTime;

        public Counter()
        {
            beginTime = DateTime.UtcNow.Ticks;
            lastTime = beginTime;
        }

        long getSpeed(long size, long time)
            => (time < ExpireTime) ? 0 : (size * 1000) / (time / TimeEx.TicksPerMS);

        long SpeedValue => getSpeed(spanSize, spanTime);
        long AvgSpeedValue => getSpeed(lastSize, lastTime - beginTime);
        int SizePercentValue => FinishSize.r100(TotalSize);
        int CountPercentValue => TotalCount <= 0 ? 0 : FinishCount * 100 / TotalCount;

        public string Speed => $"{SpeedValue.byteSize(2)}/S";
        public string AvgSpeed => $"{AvgSpeedValue.byteSize(2)}/S";
        public string Duration => (DateTime.UtcNow - new DateTime(beginTime)).ToString(@"hh\:mm\:ss\.ff");
        public string SizePair => $"{FinishSize.byteSize(2)}/{TotalSize.byteSize(2)}";
        public string CountPair => $"{FinishCount}/{TotalCount}";
        public string SizePercent => $"{SizePercentValue}%";
        public string CountPercent => $"{CountPercentValue}%";

        public string CountInfo => $"{CountPair}, {CountPercent}";
        public string FullInfo => $"{SizePercent}, {AvgSpeed}, {CountPair}, {SizePair}, {Duration}";
        public string ShortInfo => $"{SizePair}, {CountPair}, {CountPercent}";
        public string SpeedInfo => $"{Speed}, {SizePair}, {SizePercent}";

        public void update() => add(0, 0);

        public void addCount(int count = 1)
        {
            add(count, 0);
        }

        public void addSize(long size)
        {
            add(0, size);
        }

        public void add(int count, long size)
        {
            FinishCount += count;
            FinishSize += size;

            long now = DateTime.UtcNow.Ticks;
            if (now - lastTime > ExpireTime)
            {
                spanSize = FinishSize - lastSize;
                spanTime = now - lastTime;

                lastTime = now;
                lastSize = FinishSize;

                trigger();
            }
        }

        public void trigger()
        {
            TimeIsUp?.Invoke(this);
        }
    }
}
View Code

Github:

https://github.com/bsmith-zhao/vfs/blob/main/util/Counter.cs

参考:

https://github.com/bsmith-zhao/vfs/blob/main/util/Number.cs

https://github.com/bsmith-zhao/vfs/tree/main/util