- PR -

DataTableで例外

1
投稿者投稿内容
nico
会議室デビュー日: 2004/07/17
投稿数: 6
投稿日時: 2008-04-17 01:18
こんにちは。nicoと申します。

DataTableの動作について理解できないところがあり困っています。
どういう理由で例外が発生するのか分かる方がいらっしゃいましたら教えてください。
現象は下記のとおりです。

<環境>
VisialC# 2005Express(Win2000)

<データ定義>
DataSetに2つのDataTableを定義
DataTable1(Column1:主キー)
DataTable2(Column2:主キー) ※テーブル間にリレーションは無し

<処理>
2つのスレッドで各DataTableを操作
スレッド1:DataTable1のレコード消去&追加(Clear/AddRowを使用)
スレッド2:DataTable2のレコード更新(BeginLoadData/LoadDataRow/EndLoadDataを使用)

上記で動作させた場合、スレッド2でNullReferenceExceptionが発生します。
スタックトレースは下記のとおりです。
'System.NullReferenceException' の初回例外が System.Data.dll で発生しました。
場所 System.Data.DataColumn.IsNotAllowDBNullViolated()
場所 System.Data.DataSet.EnableConstraints()
場所 System.Data.DataSet.set_EnforceConstraints(Boolean value)
場所 System.Data.DataTable.EndLoadData()
場所 WindowsApplication1.Form1.ThreadProc(Object param)

各スレッドが別々のテーブルを操作しているため、競合等は問題ないと思っていたのですが、同じDataSetだと何か影響を及ぼす事があるのでしょうか?

念のため、ミニマムコードも記載します。

DataSet1 ds = new DataSet1();
Thread[] th = new Thread[2];

// スレッド開始処理
private void button1_Click(object sender, EventArgs e)
{
ParameterizedThreadStart thStart = new ParameterizedThreadStart(ThreadProc);

for (int i = 0; i < th.Length; i++)
{
th[i] = new Thread(thStart);
th[i].IsBackground = true;
th[i].Start(i);
}
}

   // スレッド処理
private void ThreadProc(object param)
{
int no = Convert.ToInt32(param);

// 現象を発生し易くするための無限ループ
while (true)
{
if ((no % 2) == 0)
{
ds.DataTable2.Clear();
ds.DataTable2.AddDataTable2Row(no.ToString());
}
else
{
ds.DataTable1.BeginLoadData();
ds.DataTable1.LoadDataRow(new object[]{no.ToString()}, LoadOption.Upsert);
ds.DataTable1.EndLoadData();
}
}
}
甕星
ぬし
会議室デビュー日: 2003/03/07
投稿数: 1185
お住まい・勤務地: 湖の見える丘の上
投稿日時: 2008-04-17 02:04
DataSetのヘルプを参照してください。

「この型は、マルチスレッド読み取り操作に対して安全です。すべての書き込み操作の同期をとる必要があります。」と書かれています。複数スレッドからの操作に対して安全性が保障されていないのに、それを行っている時点で何が起こっても不思議はないでしょう。まずはきちんと排他制御を行った上で、どうようの現象がおこるか検証してください。

コード:
 場所 System.Data.DataColumn.IsNotAllowDBNullViolated()
場所 System.Data.DataSet.EnableConstraints()
場所 System.Data.DataSet.set_EnforceConstraints(Boolean value)
場所 System.Data.DataTable.EndLoadData()
場所 WindowsApplication1.Form1.ThreadProc(Object param) 


少なくとも上記のスタックトレースを見る限りで、DataTableに対するメソッドを読んだことで、DataSetに対しても何らかの操作が行われていることは明らかですよね。
_________________
甕星 <mikahosi@abox9.so-net.ne.jp>
http://blogs.msmvp.jp/mikahosi/
nico
会議室デビュー日: 2004/07/17
投稿数: 6
投稿日時: 2008-04-17 07:39
甕星さん、返答ありがとうございます。

下記の2パターンで試してみました。
1)DataSet単位で排他制御を追加
 例外は発生しなくなりました。
2)2つのDataTableを個々に定義して、同様の処理を実施
 こちらも問題ないようです。

DataTableに対する操作(EndLoadData)は、対象のDataTableに対してのみ実施されると思っていましたが、DataSetにまとめて定義した場合は異なるのですね。
大変勉強になりました。

1

スキルアップ/キャリアアップ(JOB@IT)