【问题标题】:Timer Class not Firing before End of Program定时器类在程序结束前未触发
【发布时间】:2015-09-02 15:07:26
【问题描述】:

所以我尝试修改 Sriram Sakthivel 在这里发布的代码块:

C#: How to start a thread at a specific time

public class Program
{
    static Boolean checkIn = false;

    public static void checkInCycle()
    {
        checkIn = SynchronousSocketClient.StartClient();

        if(checkIn == false)
        {
            // Use TimeSpan constructor to specify:
            // ... Days, hours, minutes, seconds, milliseconds.
            TimeSpan span = new TimeSpan(00, 00, 30);

            //SetUpTimer(span);
            DateTime current = DateTime.Now;
            TimeSpan triggerTime  = current.TimeOfDay + span;

            SetUpTimer(triggerTime);
        }
        if(checkIn == true)
        {

            //Do some processing 
        }



    }

    public static void SetUpTimer(TimeSpan alertTime)
    {
        //DateTime current = DateTime.Now;
        //TimeSpan timeToGo = alertTime - current.TimeOfDay;

        TimeSpan timeToGo = alertTime;
        Console.WriteLine("Checking in at: " + timeToGo);

        if (timeToGo < TimeSpan.Zero)
        {
            return; //time already passed
        }
        System.Threading.Timer timer = new System.Threading.Timer(x =>
        {
            Console.WriteLine("Running CheckIn Cycle");
            checkInCycle();

        }, null, timeToGo, Timeout.InfiniteTimeSpan);

    }

    public static int Main(String[] args)
    {
        checkInCycle();

        Console.WriteLine("End of Program Reached");
        Console.ReadLine();

        return 0;

    }
}

但是,我没有指定确切的运行时间,而是尝试在当前时间上增加 30 分钟,以尝试使客户端服务在尝试再次连接之前保持活动 x 分钟。现在,为了简单/测试起见,如果它无法连接到服务器,我将其设置为每 30 秒运行一次 checkInCycle。

首先检查 SynchronousSocketClient.StartClient();如果服务器关闭,则成功返回 false,并将进入 if(checkIn == false) 循环 - 但是,在设置计时器后,它会继续处理主循环的其余部分,并在结束时等待而不触发和重新调度计时器。

关于为什么会发生这种情况的任何想法?另外我知道我可以在再次检查之前让主线程睡眠 x 分钟,但是客户端可能会睡几个小时,因此我听说计时器效率更高,是这样吗?

【问题讨论】:

  • 你的计时器可能正在被垃圾收集,因为它是一个局部变量,让它成为一个类级别的变量/字段。
  • 你的触发时间应该是span,不要加上一天中的时间。
  • 我很抱歉。您需要保留 Timer 的引用以防止它被垃圾收集。我已在原始帖子中更新了我的答案以反映这一变化。

标签: c# multithreading timer


【解决方案1】:

发生这种情况是因为在执行 main() 方法结束时,您丢失了对 System.Threading.Timer 的引用。 new System.Threading.Timer 调用在等待运行其代码时不会阻塞主线程。

在上述参考(备注下的第二个注释)中,文档解释说,必须维护一个参考(与所有托管代码一样),否则垃圾收集器不会对其进行处理。

只要您使用 Timer,就必须保留对它的引用。与任何托管对象一样,当没有对 Timer 的引用时,它会受到垃圾回收的影响。 Timer 仍然处于活动状态这一事实并不能阻止它被收集。

保持这个简单程序的方法是使用System.Threading.Monitor 并在main() 函数中的checkInCycle() 函数调用之后直接执行Wait()。然后 Pulse() 当你想继续时。

如果您对执行异步调用或在多线程环境中操作的任何更深入的主题感兴趣,您可能需要进一步阅读线程同步或并发。

*为澄清而编辑。

【讨论】:

  • Wait() 和 Pulse() 是否必要?我的意思是,现在我只是按照建议在类范围之外添加了对 Timer 的引用,并且代码只是位于 ReadLine() 上并循环计时器。这对服务来说是不好的做法吗?有没有更优雅的方法来做到这一点,而不是让程序坐在 ReadLine() 上以阻止它关闭
  • 不好的做法,不。但是,如果有人不小心按了“输入”按钮,控制台应用程序可能会关闭。 Monitor 类需要特定的意图,因为您必须将 Pulse() 方法编程为用户所需的某些操作。这听起来更像是一项服务,因此我(个人)希望明确定义我可以执行的启动和停止操作。 .NET Windows Service Application
【解决方案2】:

除了Andrew的答案,你修改SetupTimer的方式,你不应该添加当前时间,直接传递重试时间。否则你必须等待几个小时:-)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-06-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-11-10
    相关资源
    最近更新 更多