デッドロックについて説明してみる - その 2 ( C# で deadlock を例外処理でキャッチしてみる ) -
前回デッドロックのサンプルクエリを書いてみました。
ryuchan.hatenablog.com
今回は、そのクエリを C# で書いてみます。( 処理の対比がしやすいように、あえて TransactionScope 等は使用しません。 )
- クエリ 1 を C# に変更
string connectionString = @"Data Source=localhost; Initial Catalog=AdventureWorks; Connect Timeout=60; Persist Security Info=True; Integrated Security=true;"; using (SqlConnection connection = new SqlConnection(connectionString)) { connection.Open(); var command = connection.CreateCommand(); while (0 == 0) { try { var transaction = connection.BeginTransaction("query1"); command.Connection = connection; command.Transaction = transaction; command.CommandText = @"UPDATE Production.Product SET Name = N'Bearing Ball' WHERE ProductID = 2;"; command.ExecuteNonQuery(); command.CommandText = @"UPDATE Production.Product SET Name = N'Adjustable Race' WHERE ProductID = 1;"; command.ExecuteNonQuery(); transaction.Commit(); transaction.Dispose(); } catch (SqlException ex) { MessageBox.Show("エラーNo:" + ex.Number.ToString() + " エラーメッセージ:" + ex.Message.ToString()); break; } } command.Dispose(); }
- クエリ 2 を C# に変更
string connectionString = @"Data Source=localhost; Initial Catalog=AdventureWorks; Connect Timeout=60; Persist Security Info=True; Integrated Security=true;"; using (SqlConnection connection = new SqlConnection(connectionString)) { connection.Open(); var command = connection.CreateCommand(); while (0 == 0) { try { var transaction = connection.BeginTransaction("query2"); command.Connection = connection; command.Transaction = transaction; command.CommandText = @"UPDATE Production.Product SET Name = N'Adjustable Race' WHERE ProductID = 1;"; command.ExecuteNonQuery(); command.CommandText = @"UPDATE Production.Product SET Name = N'Bearing Ball' WHERE ProductID = 2;"; command.ExecuteNonQuery(); transaction.Commit(); transaction.Dispose(); } catch (SqlException ex) { MessageBox.Show("エラーNo:" + ex.Number.ToString() + " エラーメッセージ:" + ex.Message.ToString()); break; } } command.Dispose(); }
それぞれのコードを下図のような簡単なアプリケーションで実行させてみます。
しばらくすると、下図のような画面が表示されます。SQL Server Managemnet Studio で実行させた時と同じようにデッドロックが発生しました。
このままでは使いものにならないコードなので、少しコードを変更してみましょう。デッドロックが発生した場合に、少し間をおいてリトライするように変更します。
string connectionString = @"Data Source=localhost; Initial Catalog=AdventureWorks; Connect Timeout=60; Persist Security Info=True; Integrated Security=true;"; int retryCount = 10; using (SqlConnection connection = new SqlConnection(connectionString)) { connection.Open(); var command = connection.CreateCommand(); while (retryCount > 0) { try { var transaction = connection.BeginTransaction("query2"); command.Connection = connection; command.Transaction = transaction; command.CommandText = @"UPDATE Production.Product SET Name = N'Adjustable Race' WHERE ProductID = 1;"; command.ExecuteNonQuery(); command.CommandText = @"UPDATE Production.Product SET Name = N'Bearing Ball' WHERE ProductID = 2;"; command.ExecuteNonQuery(); transaction.Commit(); transaction.Dispose(); retryCount = 0; } catch (SqlException ex) { // デッドロック発生時 if (ex.Number == 1205) { Thread.Sleep(600); retryCount--; } } } command.Dispose(); }
しかし、上記のような対応をとったとしても必ずしも更新が成功するとは限りません。リソースの更新順番をすべての更新プログラムで統一するのが最善の策でしょう。
Lenovo YOGA BOOK 用のケースは既に届きました ( 一番左 )。他にもいろいろありますね。
色は黒。
インナーはフワッフワ。傷とか絶対付かなそうです。
小さいながらも、マウス、電源関係は収納できそうです。使い勝手が非常によさそう。