C#试图用秒表包裹一个功能

时间:2018-04-23 12:17:40

标签: c# stopwatch

我一直在尝试查看在我的代码中执行函数需要多长时间才能看到我可以优化的位置。现在我使用一个辅助类,它本质上是一个带有消息来检查这些的秒表。这样做的目的是,我应该能够在帮助器中包含我想要的任何方法调用,并且我将获得它的持续时间。

public class StopwatcherData
{
    public long Time { get; set; }
    public string Message { get; set; }

    public StopwatcherData(long time, string message)
    {
        Time = time;
        Message = message;
    }
}

public class Stopwatcher
{
    public delegate void CompletedCallBack(string result);

    public static List<StopwatcherData> Data { get; set; }
    private static Stopwatch stopwatch { get; set;}

    public Stopwatcher()
    {
        Data = new List<StopwatcherData>();
        stopwatch = new Stopwatch();
        stopwatch.Start();
    }

    public static void Click(string message)
    {
        Data.Add(new StopwatcherData(stopwatch.ElapsedMilliseconds, message));
    }

    public static void Reset()
    {
        stopwatch.Reset();
        stopwatch.Start();
    }
}

现在要使用它,我必须在我想要的函数之前调用Reset,以便重新启动计时器,然后在它之后调用click。

Stopwatcher.Reset()
MyFunction();
Stopwatcher.Click("MyFunction");

我已经阅读了一些关于代表和行动的内容,但我不确定如何将它们应用于这种情况。理想情况下,我会将该函数作为Stopwatcher调用的一部分传递。

//End Goal:
Stopwatcher.Track(MyFunction(), "MyFunction Time");

欢迎任何帮助。

3 个答案:

答案 0 :(得分:3)

对这样的应用程序进行分析并不是一个好主意,但如果你坚持,你至少可以做一些改进。

首先,不要重复使用Stopwatch,只需在每次需要时创建新内容。

其次,你需要处理两种情况 - 一种是你传递的代表返回值而另一种情况是没有。

由于您的Track方法是静态的 - 通常的做法是使其线程安全。非线程安全的静态方法是非常糟糕的主意。为此,您可以将消息存储在线索安全的集合中,例如ConcurrentBag,或者每次将项目添加到列表时使用lock

最后你可以得到这样的东西:

public class Stopwatcher {
    private static readonly ConcurrentBag<StopwatcherData> _data = new ConcurrentBag<StopwatcherData>();

    public static void Track(Action action, string message) {
        var w = Stopwatch.StartNew();
        try {
            action();
        }
        finally {
            w.Stop();
            _data.Add(new StopwatcherData(w.ElapsedMilliseconds, message));
        }
    }

    public static T Track<T>(Func<T> func, string message) {
        var w = Stopwatch.StartNew();
        try {
            return func();
        }
        finally {
            w.Stop();
            _data.Add(new StopwatcherData(w.ElapsedMilliseconds, message));
        }
    }
}

并像这样使用它:

Stopwatcher.Track(() => SomeAction(param1), "test");
bool result = Stopwatcher.Track(() => SomeFunc(param2), "test");

如果您要将其与异步委托(返回TaskTask<T>)一起使用 - 您需要为该情况再添加两个重载。

答案 1 :(得分:1)

是的,您可以创建一个接受任何操作作为委托的计时器功能。试试这个块:

public static long TimeAction(Action action)
{
    var timer = new Stopwatch();
    timer.Start();
    action();
    timer.Stop();
    return timer.ElapsedMilliseconds;
}

可以这样使用:

  var elapsedMilliseconds = TimeAction(() => MyFunc(param1, param2));

如果你的包装函数返回一个值,这有点尴尬,但你可以通过在闭包内分配一个变量来解决这个问题,如下所示:

  bool isSuccess ;
  var elapsedMilliseconds = TimeToAction(() => {
    isSuccess = MyFunc(param1, param2);
  });

答案 2 :(得分:0)

我前一段时间也遇到过这个问题,我总是害怕当我将Stopwatcher.Track(() => SomeFunc(), "test")(请参阅Evk的回答)更改回来时,我会留下错误SomeFunc()。所以我想要做一些包装它而不改变它的东西!

我想出了一个使用,这肯定不是预期的目的。

public class OneTimeStopwatch : IDisposable
{
    private string _logPath = "C:\\Temp\\OneTimeStopwatch.log";
    private readonly string _itemname;
    private System.Diagnostics.Stopwatch sw = new System.Diagnostics.Stopwatch();

    public OneTimeStopwatch(string itemname)
    {
        _itemname = itemname;
        sw.Start();
    }

    public void Dispose()
    {
        sw.Stop();
        System.IO.File.AppendAllText(_logPath, string.Format($"{_itemname}: {sw.ElapsedMilliseconds}ms{Environment.NewLine}"));
    }
}

这可以轻松使用

using (new OneTimeStopwatch("test"))
{
    //some sensible code not to touch
    System.Threading.Thread.Sleep(1000);
}
//logfile with line "test: 1000ms"

我只需删除2行(和自动格式)以使其再次正常。 另外,我可以轻松地在这里包裹多条线,而不用在另一种方法中定义新功能。

同样,建议不要使用几毫秒的条款。