我有一个程序,我有一个线程计时器来更新来自数据服务器的时间。但是,我注意到计时器运行了几次并且之后停止呼叫。我尝试将线程计时器代码复制到一个新的程序,它运行正常,所以我知道计时器代码必须干扰程序的其余部分,但我不知道在哪里,任何人都可以帮助我吗?
这个程序在这里张贴所有内容都很大,我试着在这里发布所有相关部分。
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));
}
任何人都可以指出干扰可能在哪里吗?
答案 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>
这样的简单事情也不是线程安全的,因此您应该同步对这些项的访问。