Тупиковая ошибка с одиночкой - PullRequest
0 голосов
/ 17 марта 2012

Я пытаюсь немного больше понять шаблоны проектирования, и я строю быстрый тест, чтобы помочь мне лучше понять шаблон синглтона.Однако я столкнулся с ошибкой в ​​.net, которая сбила меня с толку, что сделало его более странным, я не смог повторить ошибку, которая только добавила к моей путаницеПриведенный ниже код не самый лучший код в мире, но я случайным образом тестировал вещи, чтобы помочь мне глубже понять.

class Program
{

    static void Main(string[] args)
    {
        Thread t1 = new Thread(new ThreadStart(run1));
        Thread t2 = new Thread(new ThreadStart(run2));

        t1.Priority = ThreadPriority.Lowest;
        t1.Start();
        t2.Priority = ThreadPriority.Lowest;
        t2.Start();

        Console.ReadLine();
    }

    public static void run1()
    {
        Console.WriteLine("im in run1 \n");
        TestSingleton._instance.PopulateCrudItemsProcess();
        Thread.Sleep(1000);
        Console.WriteLine(TestSingleton._instance.GetStrValue("first", 1000));
    }
    public static void run2()
    {
        Console.WriteLine("im in run2 \n");
        TestSingleton._instance.PopulateCrudItemsProcess();
        Console.WriteLine(TestSingleton._instance.GetStrValue("second", 500));

    }

}

sealed class TestSingleton
{
    private TestSingleton() { }

    public static readonly TestSingleton _instance = new TestSingleton();

    public string GetStrValue(string str, int time)
    {
        return str;
    }

    public void PopulateCrudItemsProcess()
    {
        const string proc = "[testdb].[dbo].[tstsproc]";
        string _reportingConnStr = ConfigurationManager.ConnectionStrings["reporting"].ConnectionString;

        using (SqlConnection conn = new SqlConnection(_reportingConnStr))
        {
            using (SqlCommand cmd = new SqlCommand(proc, conn))
            {
                cmd.CommandType = CommandType.StoredProcedure;
                cmd.CommandTimeout = 7200;
                conn.Open();
                cmd.ExecuteNonQuery();
            }
        }
    }
}

Теперь, когда произошел сбой программы на cmd.ExecuteNonQuery ();и заявил, что на ресурсе возникла тупиковая ситуация.Я хотел бы уловить фактическую ошибку, но, как я уже сказал, я смог получить ошибку только один раз.Согласно MS этот код является потокобезопасным способом создания синглтона.Таким образом, какие-либо мысли о том, что, возможно, вызвало ошибку?любая информация с благодарностью.

Ответы [ 3 ]

5 голосов
/ 17 марта 2012

Извините, но Singleton не предназначен для обеспечения безопасности потоков. Речь идет о наличии только 1 экземпляра объекта.

Поэтому PopulateCrudItemsProcess и GetStrValue не являются потокобезопасными ...

2 голосов
/ 17 марта 2012

Вы можете попробовать использовать ключевое слово lock.
Это позволяет разработчикам создавать области кодов, которые должны быть синхронизированы между потоками приложений. Это делается для того, чтобы входящие потоки не прерывали ток до его завершения.

Итак, давайте пример:

объявите внутри вашего синглтона глобальный

private object _oLock;

переписать PopulateCrudItemsProcess следующим образом

    lock(_oLock)
    {
        using (SqlConnection conn = new SqlConnection(_reportingConnStr)) 
        { 
            using (SqlCommand cmd = new SqlCommand(proc, conn)) 
            { 
                cmd.CommandType = CommandType.StoredProcedure; 
                cmd.CommandTimeout = 7200; 
                conn.Open(); 
                cmd.ExecuteNonQuery(); 
            } 
        } 
   }
1 голос
/ 17 марта 2012

Что касается безопасности нитей в вашей ситуации, вы должны сделать выбор между:

  • Создание синглтона
  • Выполнение методов в единственном экземпляре
  • Выполнение хранимых процедур

Первая часть, которую вы получили скрытно, статический конструктор вызывается только один раз в нескольких потоках, и поэтому весь код инициализации для статических переменных также выполняется только один раз. Это подразумевается, хотя и в духе упрощения понимания, вы должны использовать более явный подход, также выполняющий инициализацию в методе get instance, который является более распространенным, см. Ниже. Однако он облегчает чтение вашего кода для других, и в этом отношении он уже работает.

Вторая часть сама по себе является поточно-ориентированной, поскольку вы не делитесь какими-либо ресурсами, как уже указывал JonSkeet.

Однако третья часть, похоже, является проблемой, как уже указывали другие. Одновременное выполнение StoredProcedures или любого другого SQL-запроса может привести к взаимоблокировке на уровне БД. В вашем случае это очень вероятно, что вы видите ошибку.

Украсть заглушку из KarlLynch и сделать создание экземпляра явным образом безопасным:

public class MySingleton
{
  private static MySingleton Instance{ get; set; }
  private static readonly object initLock;

  // Private constructor
  private MySingleton()
  {
    initLock = new object();
  }

  public static MySingleton GetInstance()
  {    
    if (Instance == null)
    {
      lock(initLock)
      {
        if (Instance == null)
        {
          Instance = new MySingleton();
        }
      }
    }    
    return Instance;
  }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...