マルチスレッドを使ったOracle デッドロックテスト( VB.NET 2005 )


  ダウンロードおよび概要




※ 最初にビルドを行って下さい



http://lightbox.on.coocan.jp/download/ORACLE_DEAD_LOCK.lzh



Oracle への接続を二つのスレッドから別々に行って、行単位の更新をデッドロックが起きるように
更新順序をコントロールして実行します。
( マルチスレッドプログラミングに必要な手法を使っています )

デッドロックが起きた場合は、ROLLBACK を行い、その結果他方のスレッドでは COMMIT が実行されます。
( Framework の OracleClient の扱いは、lightbox.db に任せています )


以下重要なキーワード
目的 : デッドロックを意図的に発生させる

スレッドの開始方法
スレッドから GUI を操作する
   1) invoke を使用する
   2) invoke 用に デリゲートを定義する
   3) 橋渡し用の GUI を操作するメソッドを作成する
   4) スレッド内より、橋渡し用の メソッドを呼ぶ
共通メモリ扱い
   メモリを確実に参照する( コンパイラの最適化を避ける )
   メモリの排他制御
メモリを使った、処理順序のコントロール

  VB.NET コード




  

Imports System.Threading
Imports lightbox.db
Imports System.Data.OracleClient

Public Class Form1

	Private stopFlgLeft As Integer = 0
	Private stopFlgRight As Integer = 0
	Private deadSequence As Integer = 0
	Private count As Integer = 0

	' ************************************************************
	' 初期処理
	' ************************************************************
	Private Sub Form1_Load(ByVal sender As System.Object, _
	ByVal e As System.EventArgs) Handles MyBase.Load

		Me.Left = 0
		Me.Top = 0

		Me.LboxGrid1.AddColumn("NO", "実行順序")
		Me.LboxGrid1.AddColumn("LEFT", "左側スレッド")
		Me.LboxGrid1.AddColumn("RIGHT", "右側スレッド")

		' 左スレッド開始
		Dim ThreadLeft As New Thread(AddressOf ThreadEntryLeft)
		ThreadLeft.Start()

		' 右スレッド開始
		Dim ThreadRight As New Thread(AddressOf ThreadEntryRight)
		ThreadRight.Start()

	End Sub

	' ************************************************************
	' スレッドの入り口( 左 )
	' ************************************************************
	Private Sub ThreadEntryLeft()

		Dim dbLeft As New DbOracle("night/XE", "ora01", "ora01")

		Dim Bridge As New ProcBridge(AddressOf SetGridData)
		Dim Query As String = Nothing

		If Not dbLeft.Connect() Then
			Me.Invoke(Bridge, New Object() {1, "接続に失敗しました"})
			Return
		End If
		Me.Invoke(Bridge, New Object() {1, "Oracle に接続しました"})
		Dim tr As OracleTransaction = dbLeft.myCon.BeginTransaction()
		Me.Invoke(Bridge, New Object() {1, "トランザクションを開始しました"})

		' stopFlgLeft と 0( 右端 ) と比較して 等しい場合は 0 と置き換える
		' つまり、0 の場合は常に 0 を戻して 0 のままである
		' 1 になった時のみループが終了する
		Do While Interlocked.CompareExchange(stopFlgLeft, 0, 0) = 0
			If Interlocked.CompareExchange(deadSequence, 0, 0) = 0 Then
				Query = "update 社員マスタ set 給与 = 給与 + 1 where 社員コード = '0001'"
				dbLeft.ExecuteTransaction(Query, tr)
				Me.Invoke(Bridge, New Object() {1, "0001 を更新(ロック)"})
				Interlocked.Exchange(deadSequence, 1)
			End If

			Thread.Sleep(1000)

			If Interlocked.CompareExchange(deadSequence, 2, 2) = 2 Then
				Query = "update 社員マスタ set 給与 = 給与 + 1 where 社員コード = '0002'"
				Me.Invoke(Bridge, New Object() {1, "0002 を更新します(待機)"})
				Dim ret As Integer = dbLeft.ExecuteTransaction(Query, tr)
				If ret = -2 Then
					Me.Invoke(Bridge, New Object() {1, dbLeft.myError})
					tr.Rollback()
					Exit Do
				Else
					Me.Invoke(Bridge, _
					 New Object() {1, "0002 を更新しました(相手が終了)"})
					Exit Do
				End If
			End If

		Loop

		Try
			tr.Commit()
		Catch ex As Exception
		End Try
		tr.Dispose()
		dbLeft.Close()
		Me.Invoke(Bridge, New Object() {1, "DB 接続終了"})
		Me.Invoke(Bridge, New Object() {1, "スレッド 終了"})


	End Sub

	' ************************************************************
	' スレッドの入り口( 右 )
	' ************************************************************
	Private Sub ThreadEntryRight()

		Dim dbRight As New DbOracle("night/XE", "ora01", "ora01")

		Dim Bridge As New ProcBridge(AddressOf SetGridData)
		Dim Query As String = Nothing

		If Not dbRight.Connect() Then
			Me.Invoke(Bridge, New Object() {2, "接続に失敗しました"})
			Return
		End If
		Me.Invoke(Bridge, New Object() {2, "Oracle に接続しました"})
		Dim tr As OracleTransaction = dbRight.myCon.BeginTransaction()
		dbRight.myCommand.Transaction = tr
		Me.Invoke(Bridge, New Object() {2, "トランザクションを開始しました"})

		' stopFlgLeft と 0( 右端 ) と比較して 等しい場合は 0 と置き換える
		' つまり、0 の場合は常に 0 を戻して 0 のままである
		' 1 になった時のみループが終了する
		Do While Interlocked.CompareExchange(stopFlgRight, 0, 0) = 0
			If Interlocked.CompareExchange(deadSequence, 1, 1) = 1 Then
				Query = "update 社員マスタ set 給与 = 給与 + 1 where 社員コード = '0002'"
				dbRight.ExecuteTransaction(Query, tr)
				Me.Invoke(Bridge, New Object() {2, "0002 を更新(ロック)"})
				Interlocked.Exchange(deadSequence, 2)
			End If

			Thread.Sleep(1000)

			If Interlocked.CompareExchange(deadSequence, 2, 2) = 2 Then
				Query = "update 社員マスタ set 給与 = 給与 + 1 where 社員コード = '0001'"
				Me.Invoke(Bridge, New Object() {2, "0001 を更新します(待機)"})
				Dim ret As Integer = dbRight.ExecuteTransaction(Query, tr)
				If ret = -2 Then
					Me.Invoke(Bridge, New Object() {2, dbRight.myError})
					tr.Rollback()
					Exit Do
				Else
					Me.Invoke(Bridge, _
					 New Object() {2, "0001 を更新しました(相手が終了)"})
					Exit Do
				End If
			End If
		Loop

		Try
			tr.Commit()
		Catch ex As Exception
		End Try
		tr.Dispose()
		dbRight.Close()
		Me.Invoke(Bridge, New Object() {2, "DB 接続終了"})
		Me.Invoke(Bridge, New Object() {2, "スレッド 終了"})

	End Sub

	' ************************************************************
	' 橋渡し用デリゲート
	' ************************************************************
	Delegate Sub ProcBridge(ByVal type As Integer, ByVal str As String)

	' ************************************************************
	' 橋渡し用実体
	' ************************************************************
	Private Sub SetGridData(ByVal type As Integer, ByVal str As String)

		Me.LboxGrid1.AddRow()
		count += 1
		Me.LboxGrid1.SetColumnText("NO", count.ToString())
		If type = 1 Then
			Me.LboxGrid1.SetColumnText("LEFT", str)
		End If
		If type = 2 Then
			Me.LboxGrid1.SetColumnText("RIGHT", str)
		End If

	End Sub

	' ************************************************************
	' 左スレッド終了
	' ************************************************************
	Private Sub Button1_Click(ByVal sender As System.Object, _
	ByVal e As System.EventArgs) Handles Button1.Click

		Interlocked.Exchange(stopFlgLeft, 1)

	End Sub

	' ************************************************************
	' 右スレッド終了
	' ************************************************************
	Private Sub Button2_Click(ByVal sender As System.Object, _
	ByVal e As System.EventArgs) Handles Button2.Click

		Interlocked.Exchange(stopFlgRight, 1)

	End Sub
End Class
  

  SELECT でデッドロックが発生するようにするには

左側と右側でそれぞれ実行している UPDATE 文を SELECT 〜 FOR UPDATE に変更します

変更前(左)
  

Query = "update 社員マスタ set 給与 = 給与 + 1 where 社員コード = '0001'"
  

変更後(左)
  

Query = "select * from 社員マスタ where 社員コード = '0001' for update"
  


変更前(右)
  

Query = "update 社員マスタ set 給与 = 給与 + 1 where 社員コード = '0002'"
  

変更後(右)
  

Query = "select * from 社員マスタ where 社員コード = '0002' for update"
  

  ロックによる参照内容の違い

相手側の更新前の値しか参照できない事を確認しています



  

Imports System.Threading
Imports lightbox.db
Imports System.Data.OracleClient

Public Class Form1

	Private stopFlgLeft As Integer = 0
	Private stopFlgRight As Integer = 0
	Private deadSequence As Integer = 0
	Private count As Integer = 0

	' ************************************************************
	' 初期処理
	' ************************************************************
	Private Sub Form1_Load(ByVal sender As System.Object, _
	ByVal e As System.EventArgs) Handles MyBase.Load

		Me.Left = 0
		Me.Top = 0

		Me.LboxGrid1.AddColumn("NO", "実行順序")
		Me.LboxGrid1.AddColumn("LEFT", "左側スレッド")
		Me.LboxGrid1.AddColumn("RIGHT", "右側スレッド")

		' 左スレッド開始
		Dim ThreadLeft As New Thread(AddressOf ThreadEntryLeft)
		ThreadLeft.Start()

		' 右スレッド開始
		Dim ThreadRight As New Thread(AddressOf ThreadEntryRight)
		ThreadRight.Start()

	End Sub

	' ************************************************************
	' スレッドの入り口( 左 )
	' ************************************************************
	Private Sub ThreadEntryLeft()

		Dim dbLeft As New DbOracle("PED0-034/ORCL", "ora_01", "ora_01")

		Dim Bridge As New ProcBridge(AddressOf SetGridData)
		Dim Query As String = Nothing

		If Not dbLeft.Connect() Then
			Me.Invoke(Bridge, New Object() {1, "接続に失敗しました"})
			Return
		End If
		Me.Invoke(Bridge, New Object() {1, "Oracle に接続しました"})
		Dim tr As OracleTransaction = dbLeft.myCon.BeginTransaction()
		dbLeft.myCommand.Transaction = tr
		Me.Invoke(Bridge, New Object() {1, "トランザクションを開始しました"})

		' stopFlgLeft と 0( 右端 ) と比較して 等しい場合は 0 と置き換える
		' つまり、0 の場合は常に 0 を戻して 0 のままである
		' 1 になった時のみループが終了する
		Do While Interlocked.CompareExchange(stopFlgLeft, 0, 0) = 0
			If Interlocked.CompareExchange(deadSequence, 0, 0) = 0 Then
				Query = "select * from 社員マスタ where 社員コード = '0001' for update"
				dbLeft.QueryAndRead(Query)
				Me.Invoke(Bridge, New Object() {1, "0001の給与:" + dbLeft.GetValue("給与")})
				Query = "update 社員マスタ set 給与 = 給与 + 1 where 社員コード = '0001'"
				dbLeft.ExecuteTransaction(Query, tr)
				Me.Invoke(Bridge, New Object() {1, "0001 を更新(ロック)"})
				Query = "select * from 社員マスタ where 社員コード = '0001'"
				dbLeft.QueryAndRead(Query)
				Me.Invoke(Bridge, New Object() {1, "0001の給与:" + dbLeft.GetValue("給与")})

				Interlocked.Exchange(deadSequence, 1)
			End If

			Thread.Sleep(1000)

			If Interlocked.CompareExchange(deadSequence, 2, 2) = 2 Then
				Query = "select * from 社員マスタ where 社員コード = '0001'"
				dbLeft.QueryAndRead(Query)
				Me.Invoke(Bridge, New Object() {1, "0001の給与:" + dbLeft.GetValue("給与")})
				Query = "select * from 社員マスタ where 社員コード = '0002'"
				dbLeft.QueryAndRead(Query)
				Me.Invoke(Bridge, New Object() {1, "0002の給与:" + dbLeft.GetValue("給与")})

				Query = "update 社員マスタ set 給与 = 給与 + 1 where 社員コード = '0002'"
				Me.Invoke(Bridge, New Object() {1, "0002 を更新します(待機)"})
				Dim ret As Integer = dbLeft.ExecuteTransaction(Query, tr)
				If ret = -2 Then
					Me.Invoke(Bridge, New Object() {1, dbLeft.myError})
					tr.Rollback()
					Exit Do
				Else
					Me.Invoke(Bridge, _
					 New Object() {1, "0002 を更新しました(相手が終了)"})
					Exit Do
				End If
			End If

		Loop

		Try
			tr.Commit()
		Catch ex As Exception
		End Try
		tr.Dispose()
		dbLeft.Close()
		Me.Invoke(Bridge, New Object() {1, "DB 接続終了"})
		Me.Invoke(Bridge, New Object() {1, "スレッド 終了"})


	End Sub

	' ************************************************************
	' スレッドの入り口( 右 )
	' ************************************************************
	Private Sub ThreadEntryRight()

		Dim dbRight As New DbOracle("PED0-034/ORCL", "ora_01", "ora_01")

		Dim Bridge As New ProcBridge(AddressOf SetGridData)
		Dim Query As String = Nothing

		If Not dbRight.Connect() Then
			Me.Invoke(Bridge, New Object() {2, "接続に失敗しました"})
			Return
		End If
		Me.Invoke(Bridge, New Object() {2, "Oracle に接続しました"})
		Dim tr As OracleTransaction = dbRight.myCon.BeginTransaction()
		dbRight.myCommand.Transaction = tr
		Me.Invoke(Bridge, New Object() {2, "トランザクションを開始しました"})

		' stopFlgLeft と 0( 右端 ) と比較して 等しい場合は 0 と置き換える
		' つまり、0 の場合は常に 0 を戻して 0 のままである
		' 1 になった時のみループが終了する
		Do While Interlocked.CompareExchange(stopFlgRight, 0, 0) = 0
			If Interlocked.CompareExchange(deadSequence, 1, 1) = 1 Then
				Query = "select * from 社員マスタ where 社員コード = '0002' for update"
				dbRight.QueryAndRead(Query)
				Me.Invoke(Bridge, New Object() {2, "0002の給与:" + dbRight.GetValue("給与")})
				Query = "update 社員マスタ set 給与 = 給与 + 1 where 社員コード = '0002'"
				dbRight.ExecuteTransaction(Query, tr)
				Me.Invoke(Bridge, New Object() {2, "0002 を更新(ロック)"})
				Query = "select * from 社員マスタ where 社員コード = '0002'"
				dbRight.QueryAndRead(Query)
				Me.Invoke(Bridge, New Object() {2, "0002の給与:" + dbRight.GetValue("給与")})

				Interlocked.Exchange(deadSequence, 2)
			End If

			Thread.Sleep(1000)

			If Interlocked.CompareExchange(deadSequence, 2, 2) = 2 Then
				Query = "select * from 社員マスタ where 社員コード = '0001'"
				dbRight.QueryAndRead(Query)
				Me.Invoke(Bridge, New Object() {2, "0001の給与:" + dbRight.GetValue("給与")})
				Query = "select * from 社員マスタ where 社員コード = '0002'"
				dbRight.QueryAndRead(Query)
				Me.Invoke(Bridge, New Object() {2, "0002の給与:" + dbRight.GetValue("給与")})

				Query = "update 社員マスタ set 給与 = 給与 + 1 where 社員コード = '0001'"
				Me.Invoke(Bridge, New Object() {2, "0001 を更新します(待機)"})
				Dim ret As Integer = dbRight.ExecuteTransaction(Query, tr)
				If ret = -2 Then
					Me.Invoke(Bridge, New Object() {2, dbRight.myError})
					tr.Rollback()
					Exit Do
				Else
					Me.Invoke(Bridge, _
					 New Object() {2, "0001 を更新しました(相手が終了)"})
					Exit Do
				End If
			End If
		Loop

		Try
			tr.Commit()
		Catch ex As Exception
		End Try
		tr.Dispose()
		dbRight.Close()
		Me.Invoke(Bridge, New Object() {2, "DB 接続終了"})
		Me.Invoke(Bridge, New Object() {2, "スレッド 終了"})

	End Sub

	' ************************************************************
	' 橋渡し用デリゲート
	' ************************************************************
	Delegate Sub ProcBridge(ByVal type As Integer, ByVal str As String)

	' ************************************************************
	' 橋渡し用実体
	' ************************************************************
	Private Sub SetGridData(ByVal type As Integer, ByVal str As String)

		Me.LboxGrid1.AddRow()
		count += 1
		Me.LboxGrid1.SetColumnText("NO", count.ToString())
		If type = 1 Then
			Me.LboxGrid1.SetColumnText("LEFT", str)
		End If
		If type = 2 Then
			Me.LboxGrid1.SetColumnText("RIGHT", str)
		End If

	End Sub

	' ************************************************************
	' 左スレッド終了
	' ************************************************************
	Private Sub Button1_Click(ByVal sender As System.Object, _
	ByVal e As System.EventArgs) Handles Button1.Click

		Interlocked.Exchange(stopFlgLeft, 1)

	End Sub

	' ************************************************************
	' 右スレッド終了
	' ************************************************************
	Private Sub Button2_Click(ByVal sender As System.Object, _
	ByVal e As System.EventArgs) Handles Button2.Click

		Interlocked.Exchange(stopFlgRight, 1)

	End Sub
End Class
  




yahoo  google  MSDN  MSDN(us)  WinFAQ  Win Howto  tohoho  ie_DHTML  vector  wdic  辞書  天気 


[dnettool]
claudebot
24/03/29 14:29:14
InfoBoard Version 1.00 : Language=Perl

1 BatchHelper COMprog CommonSpec Cprog CprogBase CprogSAMPLE CprogSTD CprogSTD2 CprogWinsock Cygwin GameScript HTML HTMLcss InstallShield InstallShieldFunc JScript JScriptSAMPLE Jsfuncs LLINK OldProg OracleGold OracleSilver PRO PRObrowser PROc PROconePOINT PROcontrol PROftpclient PROjscript PROmailer PROperl PROperlCHAT PROphp PROphpLesson PROphpLesson2 PROphpLesson3 PROphpfunction PROphpfunctionArray PROphpfunctionMisc PROphpfunctionString PROsql PROvb PROvbFunction PROvbString PROvbdbmtn PROvbonepoint PROwebapp PROwin1POINT PROwinSYSTEM PROwinYOROZU PROwindows ProjectBoard RealPHP ScriptAPP ScriptMaster VBRealtime Vsfuncs a1root access accreq adsi ajax amazon argus asp aspSample aspVarious aspdotnet aw2kinst cappvariety centura ckeyword classStyle cmaterial cmbin cmdbapp cmenum cmlang cmlistbox cmstd cmstdseed cmtxt cs daz3d db dbCommon dbaccess dnettool dos download flex2 flex3 flex4 framemtn framereq freeWorld freesoft gimp ginpro giodownload google hdml home hta htmlDom ie9svg install java javaSwing javascript jetsql jquery jsp jspTest jspVarious lightbox listasp listmsapi listmsie listmsiis listmsnt listmspatch listmsscript listmsvb listmsvc memo ms msde mysql netbeans oraPlsql oracle oracleWiper oraclehelper orafunc other panoramio pear perl personal pgdojo pgdojo_cal pgdojo_holiday pgdojo_idx pgdojo_ref pgdojo_req php phpVarious phpguide plsql postgres ps r205 realC realwebapp regex rgaki ruby rule sboard sc scprint scquest sdb sdbquest seesaa setup sh_Imagick sh_canvas sh_dotnet sh_google sh_tool sh_web shadowbox shgm shjquery shvbs shweb sjscript skadai skywalker smalltech sperl sqlq src systemdoc tcpip tegaki three toolbox twitter typeface usb useXML vb vbdb vbsfunc vbsguide vbsrc vpc wcsignup webanymind webappgen webclass webparts webtool webwsh win8 winofsql wmi work wp youtube