先の記事の例でSQLiteで操作している部分は良いのだが,おまけのような形でつけてしまったMicrosoft Chart Controlsへのバインドに問題があることが分かった.
主に問題なのは読み込みの例で出したコードである.
this.chart1.DataBind();を最初に呼ぶのは問題ないが,毎回呼ぶと相当な負荷がかかるのだ.10000点などのプロットの場合には一瞬でもCPU負荷が100%近くに上昇してしまう.
DataBindはPointsバッファの切り替えを伴うので大きな負荷になってしまう.
Timerなどで周期的に実行する場合には,一回目の書き込みにはDataBindを使用してすでにPointがあるSeriesに対しては点の追加で対応すべき.(もちろん数珀点程度の更新なら問題ない)
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Windows.Forms; using System.Data.SQLite; namespace SQLView2 { public partial class Form1 : Form { private System.Data.SQLite.SQLiteConnection cn; private DataSet ds; private System.Data.SQLite.SQLiteDataAdapter da; public Form1() { InitializeComponent(); SQLiteConnectionStringBuilder sqlConnectionSb = new SQLiteConnectionStringBuilder { DataSource = "../../../SQLite2/bin/Debug/test.db" }; this.cn = new SQLiteConnection(sqlConnectionSb.ToString()); this.cn.Open(); ds = new DataSet(); da = new SQLiteDataAdapter(); var cmd = new SQLiteCommand(cn); cmd.CommandText = "SELECT * FROM test ORDER BY time asc"; da.SelectCommand = cmd; da.Fill(ds, "test"); this.dataGridView1.DataSource = ds.Tables["test"]; chart1.Series[0].XValueMember = "time";// チャートへのバインド chart1.Series[0].YValueMembers = "value"; this.chart1.DataSource = ds.Tables["test"]; this.chart1.DataBind(); } private void timer1_Tick(object sender, EventArgs e) { if (ds.Tables["test"].Rows.Count > 0) { DateTime last = (DateTime)ds.Tables["test"].Rows[ds.Tables["test"].Rows.Count - 1][0]; var cmd = new SQLiteCommand(cn); cmd.CommandText = "SELECT * FROM test WHERE time > " + $"'{last.ToString("yyyy-MM-dd HH:mm:ss.fff")}'" + " ORDER BY time asc"; da.SelectCommand = cmd; } this.da.Fill(ds, "test"); this.chart1.DataBind(); } private void Form1_FormClosing(object sender, FormClosingEventArgs e) { this.cn.Close(); } } }
おそらく以下のように直すとよい.
private void timer1_Tick(object sender, EventArgs e) { if (ds.Tables["test"].Rows.Count > 0) { Int32 newDataStartingIndex = ds.Tables["test"].Rows.Count; DateTime last = (DateTime)ds.Tables["test"].Rows[ds.Tables["test"].Rows.Count - 1][0]; var cmd = new SQLiteCommand(cn); cmd.CommandText = "SELECT * FROM test WHERE time > " + $"'{last.ToString("yyyy-MM-dd HH:mm:ss.fffffff")}'" + " ORDER BY time asc"; da.SelectCommand = cmd; this.da.Fill(ds, "test"); for (int i = newDataStartingIndex; i < ds.Tables["test"].Rows.Count; i++) { for (int j = 0; j < this.chart1.Series.Count; j++) { this.chart1.Series[j].Points.AddXY(ds.Tables["test"].Rows[i][0], ds.Tables["test"].Rows[i][j + 1]); } } } else { var cmd = new SQLiteCommand(cn); cmd.CommandText = "SELECT * FROM test ORDER BY time asc"; da.SelectCommand = cmd; this.da.Fill(ds, "test"); this.chart1.DataBind(); } }
時々,書き込みのタイミングと読み込みのタイミングが連続で一致してしまうとBlockに関する例外が発生する.少しタイミングをずらして再読み込みを試みるようにした方が確実のようだ.少しバッファなどを設けて長時間SQLiteの書き込みが連続しないようにしておくとよい(複数行の書き込みはトランザクションにしないとかえってブロック時間が長くなる).