【问题标题】:C# Best Way to run a function every second, Timer vs Thread?C#每秒运行一个函数的最佳方式,Timer vs Thread?
【发布时间】:2015-02-16 16:02:47
【问题描述】:

我目前正在使用线程在控制台应用程序中每秒运行一个函数 C# 是线程的最佳方式吗?正如我问过很多朋友一样,他们建议使用计时器而不是线程来每秒运行一些东西?如果使用计时器是一个更好的选择,我将如何使用计时器每秒运行一个函数?我环顾四周,但我真的不确定这是否适用,以及它是否以我自己的方式处理它的正确方式。那么有人可以告诉我答案以及我该怎么做吗?

那么每秒运行它的最佳方式是什么?在你回答之前,让我让你知道这是每秒运行 2 天...... 我当前的线程编码

namespace Reality.Game.Misc
{
    using Reality;
    using Reality.Communication.Outgoing;
    using Reality.Game.Rooms;
    using Reality.Game.Sessions;
    using Reality.Storage;
    using System;
    using System.Data;
    using System.Threading;

    public static class JobCacheWorker
    {
        private static Thread jWorkerThread;

        public static void HandleExpiration(Session Session)
        {
             using (SqlDatabaseClient client = SqlDatabaseManager.GetClient())
            {
                Session.CharacterInfo.UpdateWorking(client, 0);
            }
        }

        public static void Initialize()
        {
            jWorkerThread = new Thread(new ThreadStart(JobCacheWorker.ProcessThread));
            jWorkerThread.Priority = ThreadPriority.Highest;
            jWorkerThread.Name = "JobCacheWorker";
            jWorkerThread.Start();
        }

        public static void CheckEffectExpiry(Session Session)
        {
            try
            {
                //RunMyCodeHere...
            }
            catch (Exception exception)
            {
                    Console.WriteLine("Exception - JobCacheWorker -> " + exception.Message);
            } 
        }

        private static void ProcessThread()
        {
            while (Program.Alive)
            {
                try
                {
                    foreach (Session Session in SessionManager.Sessions.Values)
                    {
                        if (Session != null && Session.Authenticated && !Session.Stopped)
                        {
                            CheckEffectExpiry(Session);
                            Thread.Sleep(1000);
                        }
                    }
                }
                catch (ThreadAbortException exception)
                {
                    Output.WriteLine("ThreadAbortException - JobCacheWorker -> " + exception.Message);
                }
                catch (ThreadInterruptedException exception2)
                {
                    Output.WriteLine("ThreadInterruptedException - JobCacheWorker -> " + exception2.Message);
                }
            }
         }
    }
}

【问题讨论】:

  • 要考虑的主要事情是您当前的代码实际上并不是每秒都运行。如果CheckEffectExpiry 需要半秒,那么您将每 1.5 秒运行一次(进程半秒,然后休眠 1 秒,然后重复)。计时器可以让您每秒运行一个方法,但是您需要确保如果前一个仍在运行,您不会开始堆叠同时运行的多个方法(假设这是您不想)。您可能想澄清“每一秒”中精确度的重要性。
  • 在这种情况下,由于 1 秒的小延迟,睡眠比定时器更安全。
  • 视情况而定。如果您当前的方法有效,那么您忘记说明为什么它足够好 无效。另请注意:Thread.Sleep 不包括函数本身执行所花费的时间,而Timer 将为您提供连续准确的事件。准确性值得商榷,但Thread.Sleep 在 1000 次迭代后会增加(累积)不准确性,而Timer 不会。
  • 几天后我的线程崩溃并停止工作。这正常吗?
  • 是的,如果它崩溃了,那么它可能是由于您的代码中的一些错误。坠机的性质是什么?

标签: c# multithreading timer


【解决方案1】:

我会使用System.Threading.Timer,因为它通常比专用线程使用更少的资源。这是一个例子:

using System;
using System.Threading;

namespace Demo
{
    public static class Program
    {
        public static void Main()
        {
            Console.WriteLine("Starting timer with callback every 1 second.");

            Timer timer = new Timer(callback, "Some state", TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(1));

            Thread.Sleep(4500); // Wait a bit over 4 seconds.

            Console.WriteLine("Changing timer to callback every 2 seconds.");

            timer.Change(TimeSpan.FromSeconds(2), TimeSpan.FromSeconds(2));

            Thread.Sleep(9000);

            timer.Change(-1, -1); // Stop the timer from running.

            Console.WriteLine("Done. Press ENTER");

            Console.ReadLine();
        }

        private static void callback(object state)
        {
            Console.WriteLine("Called back with state = " + state);
        }
    }
}

这是控制台应用程序的不错选择。但是,您当然必须记住,回调仍然是在与主线程不同的线程上完成的,因此您必须小心同步回调和主线程之间共享的资源和变量。

【讨论】:

  • 如何让这个每秒运行一次?我是 C# 新手
  • @AshSimpson Timer timer = new Timer(callback, "Some state", TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(1)); 行使其每秒运行一次。运行代码并检查输出。
  • @MatthewWatson:如果您能够详细说明为什么 Timer 使用的资源比线程少,或者只是因为它们都使用额外的线程但计时器只会它在运行时是否存在,而另一个线程会一直存在?
  • @Chris 是的,你是对的 - 计时器只会重用/创建线程池线程来发出回调,并且当计时器代码返回时,该线程将被释放。
【解决方案2】:

您可以查看System.Threading.Timer,它定期在线程池上执行单个回调,这使得它比线程更准确。 您的代码如下所示

static void Main(string[] args)
{
    TimerCallback tmCallback = CheckEffectExpiry; 
    Timer timer =  new Timer(tmCallback,"test",1000,1000);
    Console.WriteLine("Press any key to exit the sample");
    Console.ReadLine(); 
}

static  void CheckEffectExpiry(object objectInfo)
{
    //TODO put your code
}

【讨论】:

    【解决方案3】:

    如果您希望任务与任何当前任务并行运行,那么使用线程不是一个坏选择。虽然,由于任务是每秒发生一次,我想这是一个非常小的任务。在这种情况下,最好使用计时器,因为代码更少,更容易理解。

    为此,您可以参考 System Timer 类。它有一个例子和一切。
    但总的来说,这些是您将要做的事情:

    1. 创建一个定时器实例(最好全局声明并在 Initialize() 中初始化)
    2. 设置间隔(1 秒 = 1000 毫秒)
    3. 启用计时器
    4. 开始吧
    5. 如果需要,您甚至可以停止它

    【讨论】:

    • “如果您希望任务与任何当前任务并行运行,那么使用线程不是一个坏选择”您认为 Timer 在哪里执行代码?
    • 通常,由于每件事都是按顺序移动的,因此您的程序倾向于等待当前函数完成才能继续执行,但是使用线程,程序的其余部分可以继续工作,而线程完成其给定的任务。这就是不允许也不推荐在线程和主程序之间交换数据的原因之一。
    • @Sanketh.K.Jain:Voo 的观点是计时器也在单独的线程上运行,所以这并不是两者之间的真正点。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2022-01-14
    • 2012-06-04
    • 2014-03-05
    • 2012-11-14
    • 2018-11-21
    • 2019-03-11
    相关资源
    最近更新 更多