読者です 読者をやめる 読者になる 読者になる

都内で働くSEの技術的なひとりごと

都内でサラリーマンやってます。マイクロソフト系(たまに、OSS系などマイクロソフト以外の技術も...)の技術的なことについて書いています。日々の仕事の中で、気になったことを技術要素関係なく気まぐれに選んでいるので記事内容は開発言語、インフラ等ばらばらです。なお、当ブログで発信、発言は私個人のものであり、所属する組織、企業、団体等とは何のかかわりもございません。ブログの内容もきちんと検証して使用してください。よろしくお願いします♪

デッドロックについて説明してみる - その 2 ( C# で deadlock を例外処理でキャッチしてみる ) -

SQL Server C#

 前回デッドロックのサンプルクエリを書いてみました。
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();
    }
    

 それぞれのコードを下図のような簡単なアプリケーションで実行させてみます。
f:id:koogucc11:20161016165121p:plain
 
 しばらくすると、下図のような画面が表示されます。SQL Server Managemnet Studio で実行させた時と同じようにデッドロックが発生しました。
f:id:koogucc11:20161016165041p:plain

 このままでは使いものにならないコードなので、少しコードを変更してみましょう。デッドロックが発生した場合に、少し間をおいてリトライするように変更します。

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 用のケースは既に届きました ( 一番左 )。他にもいろいろありますね。

 色は黒。
f:id:koogucc11:20161016171251j:plain

 インナーはフワッフワ。傷とか絶対付かなそうです。
f:id:koogucc11:20161016171327j:plain

 小さいながらも、マウス、電源関係は収納できそうです。使い勝手が非常によさそう。
f:id:koogucc11:20161016171354j:plain