【问题标题】:C# separate thread that calls a method on a timer (Timer out of scope)C# 调用定时器上的方法的单独线程(定时器超出范围)
【发布时间】:2013-12-08 04:18:38
【问题描述】:

我的程序有一个工作版本,我的程序的主要部分使用 forms.timer 在与 GUI 相同的线程上运行。

我正在尝试升级到使用 2 个线程的更好版本。我的一个线程(处理所有信息)需要重复调​​用(在计时器上)。

我正在使用我的主要课程中的以下内容:

Timer tmr = new Timer(new TimerCallback(bot.refresh), null, 0, 1000);

其中 bot.refresh 是我希望每秒(1000 毫秒)调用一次的方法,直到我停止它为止。

public void refresh(Object obj)
{
        Debug.WriteLine("Updated: " + DateTime.Now.ToString());
}

大约 1/3 的时间计时器将停止运行,我将得到以下输出:

Updated: 23/11/2013 4:37:24 PM
Updated: 23/11/2013 4:37:25 PM
Updated: 23/11/2013 4:37:26 PM
Updated: 23/11/2013 4:37:27 PM
Updated: 23/11/2013 4:37:28 PM
Updated: 23/11/2013 4:37:29 PM
Updated: 23/11/2013 4:37:30 PM
The thread '<No Name>' (0x3f20) has exited with code 0 (0x0).
The thread '<No Name>' (0x37a0) has exited with code 0 (0x0).

关于为什么会发生这种情况的任何建议?

另外,有没有更好的方法可以做到这一点?我觉得这是一种奇怪的方法。

感谢您的帮助。

编辑:

这会超出范围吗?这样我可以在需要时而不是在构造 Main 时启动计时器。是否需要在构造函数中才能被引用?

class Main
{
    Timer tmr;
    public Main()
    {

    }

    public void start()
    {
         tmr = new Timer (new TimerCallback(bot.refresh), null, 0, 1000);
    }
}

谢谢。

【问题讨论】:

  • 你能再解释一下这个问题吗?
  • 线程计时器 (tmr) 随机停止。 (我在这篇文章中使用的刷新方法与我在程序中使用的相同)。无论如何,我觉得有更好的方法来做到这一点。
  • 是的,您更新的示例不会超出范围,因为您在创建它的函数之外保留了对 tmr 中的变量 tmr 的引用。但是,如果您调用 Start()不止一次你会失去对第一个计时器的引用,它最终会自行停止。还要注意,你真的应该去看看the .NET naming guidelines,你的函数名真的应该以大写字母开头。
  • @ScottChamberlain 感谢您的帮助。我真的很感激。

标签: c# multithreading timer scope producer


【解决方案1】:

您的计时器超出范围,最终被垃圾收集并停止运行。您必须保留对它的引用,以免超出范围。

class MainClass
{
    public MainClass()
    {
        tmr = new Timer(new TimerCallback(bot.refresh), null, 0, 1000);
    }

    Timer tmr;
}

至于更好的处理方式,这取决于bot.refresh 在做什么。但是,如果可能的话,我会使用消费者 - 生产者模型并收集第二个线程刚进入时处理的“排队工作”(这可能对你根本不起作用,就像我说的那样,这取决于bot.refesh有效。)

class MainClass
{
    public MainClass()
    {
        bot = new Bot();
        botDataProducer = BotDataProducer();
        dataQueue = new BlockingCollection<BotData>();
        consumer = Task.Factory.Run(() => ProcessData, TaskCreationOptions.LongRunning);
        producer = Task.Factory.Run(() => GenerateData, TaskCreationOptions.LongRunning);

    }

    BlockingCollection<BotData> dataQueue;
    Task consumer;
    Task producer;
    Bot bot;
    BotDataProducer botDataProducer;

    private void ProcessData()
    {
        //blocks the thread when there is no data, automatically wakes up when data is added.
        foreach(var data in dataQueue.GetConsumingEnumerable())
        {
            bot.Refresh(data);
        }
    }

    private void GenerateData()
    {
        //Assume Next() returns "false" when there will be no more data to process
        // and blocks when there is no data currently but more could come.
        while(botDataProducer.Next())
        {
            dataQueue.Add(botDataProducer.Data);
        }

        //This allows the foreach loop in the other thread to exit when the queue is empty.
        dataQueue.CompleteAdding();
    }
}

【讨论】:

  • 我认为这一定与垃圾收集有关。我认为这个计时器会在您创建它的实例后立即启动,但我需要它等到我启动机器人,有没有解决方案?
  • 编辑了问题。 @Scott 感谢您的帮助
  • Consumer-Producer 模型并不直接适用于 refresh 正在做的事情,但我想我可以稍后将其用于其他部分。感谢您的精彩帖子。
  • 我认为我实际上可以实现这一点。我认为我遇到的唯一问题是我需要定期调用“生成数据”方法(在计时器上)。这是自动完成的吗?或者我应该在计时器上调用“生成数据”方法吗?再次感谢。
  • 如果它需要在计时器上,那就把它放在计时器上。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-01-13
  • 2015-01-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多