C# と VB.NET の質問掲示板

ASP.NET、C++/CLI、Java 何でもどうぞ

C# と VB.NET の入門サイト

Re[2]: SqlCommandをスレッド実行したときの動作について


(過去ログ 131 を表示中)

[トピック内 3 記事 (1 - 3 表示)]  << 0 >>

■77404 / inTopicNo.1)  SqlCommandをスレッド実行したときの動作について
  
□投稿者/ あゆ (1回)-(2015/10/16(Fri) 19:28:11)

分類:[.NET 全般] 

SqlCommandを使用し、PK が IDENTITY のフィールドのテーブルに insert文を実行し、
そのときに採番される PK値 を取得するときに、
複数のスレッドで実行するとエラーが発生しました。

ですが、なぜエラーになるのか分からないので、
お分かりになる方がいらっしゃったら教えてください。

環境は
言語:Microsoft Visual C# 2013
DB:Sqlserver 2008
OS:Windows 2008R2
です。

現象とすると、冒頭に書いた通り、
=========================================================
PK が IDENTITY のフィールドのテーブルに insert文を実行し、
そのときに採番される PK値 を取得する
=========================================================
と言うことを、メインスレッド+サブスレッドで並行に実行すると問題ないのですが、
メインスレッドは何もせず、サブスレッド×2で並行に実行すると、
System.InvalidOperationExceptionが発生し、
追加情報として、「この Command に関連付けられている DataReader が既に開かれています」
のエラーは発生します。

サンプルコードは下記の通りです。
========================================================
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Data.SqlClient;
using System.Diagnostics;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}

private void Form1_Load(object sender, EventArgs e)
{
Debug.WriteLine("step1");

//メインスレッドと別スレッドで並行して登録を行う(DB/ID/PW 等は伏せてます)
using (var conn1 = new SqlConnection("data source=xxx.xxx.xxx.xxx;initial catalog=xxxxx;integrated security=false;persist security info=True;User ID=xxxxx;Password=xxxxx;Pooling=false"))
using (var conn2 = new SqlConnection("data source=xxx.xxx.xxx.xxx;initial catalog=xxxxx;integrated security=false;persist security info=True;User ID=xxxxx;Password=xxxxx;Pooling=false"))
{
conn1.Open();
conn2.Open();

using (SqlCommand cmd = new SqlCommand())
{
cmd.Connection = conn1;
//必要に応じて実行
cmd.CommandText = "drop table test1";
cmd.ExecuteNonQuery();

cmd.CommandText = "create table test1(f1 int IDENTITY(1,1)PRIMARY KEY, f2 varchar(10), f3 int)";
cmd.ExecuteNonQuery();
}

ActionClass action1 = new ActionClass(conn1, "action1");

Task task1 = new Task(action1.Action);

task1.Start();

ActionClass action2 = new ActionClass(conn2, "action2");
action2.Action();

task1.Wait();
}

Debug.WriteLine("step2");

//メインスレッドはただ待つだけ。2つのスレッドが並行して登録を行う
using (var conn1 = new SqlConnection("data source=xxx.xxx.xxx.xxx;initial catalog=xxxxx;integrated security=false;persist security info=True;User ID=xxxxx;Password=xxxxx;Pooling=false"))
using (var conn2 = new SqlConnection("data source=xxx.xxx.xxx.xxx;initial catalog=xxxxx;integrated security=false;persist security info=True;User ID=xxxxx;Password=xxxxx;Pooling=false"))
{
conn1.Open();
conn2.Open();

ActionClass a1 = new ActionClass(conn1, "action1");
ActionClass a2 = new ActionClass(conn2, "action2");

Task task1 = new Task(a1.Action);
Task task2 = new Task(a1.Action);

task1.Start();
task2.Start();

task1.Wait();
task2.Wait();
}

}

class ActionClass
{
SqlConnection conn;
string id;

internal ActionClass(SqlConnection conn, string id)
{
this.conn = conn;
this.id = id;
}

internal void Action()
{
using (SqlCommand cmd = new SqlCommand())
{
cmd.Connection = conn;
for (int i = 0; i < 100; i++)
{
cmd.CommandText = string.Format("insert test1(f2, f3) values ('{0}', {1}); select SCOPE_IDENTITY();", id, i);
// step2が出力された後、ここでエラーが発生する
Debug.WriteLine(id + ":" + cmd.ExecuteScalar());
}
}
}
}
}
}
========================================================

上記を実行すると、イミディエイトウィンドウには下記のように表示されます
(一部編集しています)
========================================================
step1
action2:1
action1:2
action2:3
action1:4
:action1とaction2のメッセージの繰り返し
action2:97
action1:98
action1:100
action2:99
step2
action1:102
action1:
action1:101
action1:103
action1:104
action1:105
action1:106
action1:107
action1:108
action1:109
action1:110
action1:111
action1:112
action1:113
action1:114
action1:115
action1:116
action1:117
action1:118
action1:119
action1:120
型 'System.InvalidOperationException' の初回例外が System.Data.dll で発生しました
action1:121
========================================================

step1とstep2で行っていることは同じだと思うのですが、
なぜ、step2では
========================================================
System.InvalidOperationException
この Command に関連付けられている DataReader が既に開かれています
========================================================
が発生するのでしょうか?
複数のTaskで実行すると、エラーメッセージ通りの同一のDataReaderが使用されるということでしょうか?

それとも環境に依存するのでしょうか?

お分かりになる方がいらっしゃったら、ご回答よろしくお願いします。
引用返信 編集キー/
■77408 / inTopicNo.2)  Re[1]: SqlCommandをスレッド実行したときの動作について
□投稿者/ 魔界の仮面弁士 (534回)-(2015/10/17(Sat) 01:31:38)
No77404 (あゆ さん) に返信
> Task task1 = new Task(a1.Action);
> Task task2 = new Task(a1.Action);

a2.Action では無く?
引用返信 編集キー/
■77409 / inTopicNo.3)  Re[2]: SqlCommandをスレッド実行したときの動作について
□投稿者/ あゆ (2回)-(2015/10/17(Sat) 15:01:13)
>>Task task1 = new Task(a1.Action);
>>Task task2 = new Task(a1.Action);
>
> a2.Action では無く?

…あっ!! 恥ずかしい。。。
ご指摘の通り通りでした。

お騒がせしました。。。

解決済み
引用返信 編集キー/


トピック内ページ移動 / << 0 >>

このトピックに書きこむ

過去ログには書き込み不可

管理者用

- Child Tree -