线程计时器不工作,为什么?

时间:2012-07-31 18:59:30

标签: c# multithreading timer

我有一个程序,我有一个线程计时器来更新来自数据服务器的时间。但是,我注意到计时器运行了几次并且之后停止呼叫。我尝试将线程计时器代码复制到一个新的程序,它运行正常,所以我知道计时器代码必须干扰程序的其余部分,但我不知道在哪里,任何人都可以帮助我吗?

这个程序在这里张贴所有内容都很大,我试着在这里发布所有相关部分。

public partial class HistoricalDownload : Form
{
    static int column = 2;
    static int row = 100;

    string timeFmt = "yyyy/MM/dd HH:mm:ss.fff";

    ZenFire.Connection zf;
    ZenFire.Connection.TickEventHandler tick;
    ZenFire.IProduct product = null;

    System.Windows.Forms.TextBox[,] textbox = new System.Windows.Forms.TextBox[column, row];
    DisplayTimer displayTimer = new DisplayTimer();
    memoryStreamClass msc = new memoryStreamClass();
    Dictionary<string, int> dictionarySymbol = new Dictionary<String, int>();
    delegate void StringParameterDelegate(int j, string value);

    public HistoricalDownload(ZenFire.Connection z)
    {
        InitializeComponent();

        int month = 0;
        int year = 0;

        string symbol;
        string exchange;
        string finalSymbol;

        string[] lineSplit;

        zf = z;
        tick = new ZenFire.Connection.TickEventHandler(zf_TickEvent);
        zf.TickEvent += tick;

                //set the array for name and update time 

        for (int k = 0; k < column; k++)
        {
            for (int j = 0; j < row; j++)
            {
                textbox[k, j] = new System.Windows.Forms.TextBox();
                textbox[k, j].Size = new Size(140, 18);
                textbox[k, j].Name = "textbox_" + k + "_" + j;

                if (j >= 50)
                {
                    textbox[k, j].Location = new System.Drawing.Point((k * 140) + 400, ((j - 50) * 18) + 30);
                }
                else
                {
                    textbox[k, j].Location = new System.Drawing.Point((k * 140) + 20, (j * 18) + 30);
                }

                textbox[k, j].Visible = true;
                Controls.Add(textbox[k, j]);
            }
        }

                //load the config file and subscribe the symbol

                    ....

                ///////////////////////////////////////

        System.Threading.TimerCallback displayCallback = new System.Threading.TimerCallback(timeDisplay);
        System.Threading.Timer displayTimerThread = new System.Threading.Timer(displayCallback, displayTimer, 0, 1000);
    }

    public void timeDisplay(object timerObject)
    {
        DisplayTimer t = (DisplayTimer)timerObject;


        for (int j = 0; j < t.row; j++)
        {
            string value = t.outputTime[j].ToString(timeFmt);

            if (value != "0001/01/01 00:00:00.000")
            {
                writeToTextBox(j, value);
            }
        }
    }

    public void writeToTextBox(int j, string value)
    {
        if (InvokeRequired)
        {
            BeginInvoke(new StringParameterDelegate(writeToTextBox), new object[] { j, value });
            return;
        }
        //// Must be on the UI thread if we've got this far
        textbox[1, j].Text = value;
    } 

    void zf_TickEvent(object sender, ZenFire.TickEventArgs e)
    {
        string product = e.Product.ToString();
        int c = dictionarySymbol[product];

        displayTimer.outputTime[c] = e.TimeStamp;

        msc.fillBuffer(string.Format("{0},{1},{2},{3},{4}\r\n",
                        e.TimeStamp.ToString(timeFmt),
                        product,
                        Enum.GetName(typeof(ZenFire.TickType), e.Type),
                        e.Price,
                        e.Volume));
    }

任何人都可以指出干扰可能在哪里吗?

2 个答案:

答案 0 :(得分:1)

如果您在计时器回调中所做的一切都在更新用户界面,我建议您改用System.Windows.Forms.Timer。您不必处理InvokeRequired / BeginInvoke,因为该处理程序在UI线程上运行。

您似乎也在为System.Thread.Timer使用本地变量。这可能导致在执行HistoricalDownload后完成计时器。这可能比你希望计时器停止运行要早得多。 (请参阅http://msdn.microsoft.com/en-us/library/saba8ksx处的第一个注释)您应该将该变量放在父类的字段中 - 或者只要您希望计时器运行,任何类都将保持“活动”状态。如果您使用System.Windows.Forms.Timer,我认为这不会是一个问题。但是,对于那些异步使用的东西来说,保留一个字段也是一个好主意。

答案 1 :(得分:0)

计时器的Tick事件可能在ThreadPool线程上发生(许多框架计时器执行此操作,但由于您使用的是自定义计时器(ZenFire?),这是不可能确定的。)

如果是这种情况,您的代码可能不是线程安全的,并且您可能在计时器的Tick事件中收到异常。同样,根据实现,异常可能会阻止计时器在该点之后正常运行。

要注意的具体事项是不更新计时器的Tick事件中的任何UI组件 - 而是安装,使用Control.Invoke将调用编组回UI线程(如果你是Dispatcher.Invoke使用WPF)。此外,使用Dictionary<T,U>这样的简单事情也不是线程安全的,因此您应该同步对这些项的访问。