2014-12-16 4 views
1

В приложении, которое я разрабатываю, я буду использовать 2 потока для выполнения различных операций. (Я не буду вдаваться в подробности здесь.) Эти потоки работают в циклах, проверяя, есть ли работа, которая должна быть выполнена, выполняя работу, вычисляя время, необходимое для ожидания и ожидания. (Смотрите ниже)Могу ли я вызвать Monitor.Pulse из другого класса в C#

public Global : System.Web.HttpApplication 
{ 
    private static Thread StartingDateThread; 
    private static Thread DeadlineDateThread; 
    private static object o1; 
    private static object o2; 

    public static Thread GetStartingDateThreadInstance 
    { 
    get 
     { 
     if(StartingDateThread==null) 
      { 
      StartingDateThread=new Thread(new ThreadStart(MonitorStartingDates)); 
      } 
      return StartingDateThread; 
     } 
    } 

public static Thread GetDeadlineThreadInstance 
    { 
    get 
     { 
     if(DeadlineDateThread==null) 
      { 
      DeadlineDateThread=new Thread(new ThreadStart(MonitorDeadlines)); 
      } 
      return DeadlineDateThread; 
     } 
    } 

public static object GetFirstObjectInstance 
    { 
    get 
     { 
     if(o1==null) 
      { 
      o1=new object(); 
      } 
      return o1; 
     } 
    } 

public static object GetSecondObjectInstance 
    { 
    get 
     { 
     if(o2==null) 
     { 
      o2=new object(); 
     } 
     return o2; 
     } 
    } 

    protected void Application_Start(object sender, EventArgs e) 
    { 
     GetStartingDateThreadInstance.Start(); 
     GetDeadlineThreadInstance.Start(); 
     ////////////////////// 
     ////Do other stuff. 
    } 


    public void MonitorStartingDates() 
    { 
     while(true) 
     { 
      //Check if there is stuff to do. 
      //Do stuff if available. 
      //Check if there will be stuff to do in the future and if there is, check 
      //the time to wake up. 
      //If there is nothing to do, sleep for a pre-determined 12 hours. 

     if(StuffToDoInFuture) 
     { 
      Monitor.Enter(GetFirstObjectInstance); 
      Monitor.Wait(WaitingTime); 
      Monitor.Exit(GetFirstObjectInstance); 
     } 
     else 
     { 
      Monitor.Enter(GetFirstObjectInstance); 
      Monitor.Wait(new TimeSpan(12, 0, 0)); 
      Monitor.Exit(GetFirstObjectInstance); 
     } 
     } 
    } 

    public void MonitorDeadlines() 
    { 
     while(true) 
     { 
      //Check if there is stuff to do. 
      //Do stuff if available. 
      //Check if there will be stuff to do in the future and if there is, check 
      //the time to wake up. 
      //If there is nothing to do, sleep for a pre-determined 3 days and 12 hours. 

     if(StuffToDoInFuture) 
     { 
      Monitor.Enter(GetSecondObjectInstance); 
      Monitor.Wait(WaitingTime); 
      Monitor.Exit(GetSecondObjectInstance); 
     } 
     else 
     { 

      Monitor.Enter(GetSecondObjectInstance); 
      Monitor.Wait(new TimeSpan(3, 12, 0, 0)); 
      Monitor.Exit(GetSecondObjectInstance); 
     } 

     } 
    } 

Как вы можете видеть, что эти две нити запускаются в методе Application_Start в файле asax. Они работают, если есть вещи, которые можно сделать, и затем они подсчитывают время, которое им нужно подождать, а затем они ждут. Однако, поскольку пользователи веб-приложения выполняют операции, новые записи будут вставлены в базу данных, и будут ситуации, когда любой из двух потоков должен будет возобновить работу раньше запланированного. Итак, скажем, у меня есть метод в моем классе DataAccess, который вставляет в базу данных новые данные. (См ниже)

public class DataAccess 
    { 
     /////////////// 
     // 
     public void InsertNewAuction() 
     { 
     ///Insert new row calculate the time 
      Monitor.Pulse(Global.GetFirstObjectInstance); 
      Monitor.Pulse(Global.GetSecondObjectInstance); 
     /// 
     } 
    } 

Похоже, что это недопустимая операция, потому что на той стадии, когда Monitor.Pulse вызывается из метода InsertNewAuction я получаю исключение. Что-то вроде «Метод синхронизации объекта был вызван из несинхронизированного блока кода». Есть ли способ сделать это? Спасибо за вашу помощь.

+1

Почему бы не использовать модель публикации/подписки? ala ServiceBus – tvanfosson

ответ

2

Что касается конкретной ошибки, которую вы видите, это потому, что Monitor.Pulse необходимо вызвать внутри блокировки монитора, как это (я использовал блокировку вместо Enter/Exit, так как это безопаснее для обеспечения замок всегда освобожден, так как он использует правильную TRY/наконец блок):

lock (Global.GetFirstObjectInstance) 
{ 
    Monitor.Pulse(Global.GetFirstObjectInstance); 
} 

что касается более общей конструкции вопрос здесь, это часто опасно подвергать объекты замок, как общественный (или еще хуже, глобальные) поля. В частности, это может быть рецепт взаимоблокировок, когда несколько глобальных блокировок подвергаются воздействию и приобретаются в разных порядках или когда у вас есть случаи блокировки отправки в поток пользовательского интерфейса при сохранении блокировки. Подумайте об альтернативных способах достижения того, что вам нужно.

0

Как указано в другом ответе, вы должны приобрести замок, прежде чем вы сможете позвонить Monitor.Pulse() на объект монитора.

Таким образом, ваш код имеет как минимум еще одну серьезную ошибку: вы не инициализируете объект синхронизации поточно-безопасным способом, что может легко привести к двум различным потокам с использованием двух разных экземпляров объектов, что не приведет к синхронизации между эти темы:

public static object GetFirstObjectInstance 
{ 
    get 
    { 
     if(o1==null) 
     { 
      o1=new object(); 
     } 
     return o1; 
    } 
} 

Если две нити называют этот геттер одновременно, каждый из них может увидеть o1 как null и попытаться инициализировать его. Затем каждый может вернуть другое значение для экземпляра объекта.

Вы должны просто инициализировать объект в инициализаторе:

private static readonly object o1 = new object(); 

И затем вернуть его из геттер:

public static object GetFirstObjectInstance { get { return o1; } } 

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

Было бы также лучше, если бы свойство соответствовало нормальным соглашениям об именах .NET. Метод, возвращающий объект, будет иметь «Get» в имени, но свойство не будет. Просто назовите его «FirstObjectInstance».

Также, как отметил Дэн, используйте lock везде, где вы хотите приобрести замок.

В коде могут быть и другие проблемы ... Я не сделал подробного обзора. Но выше вы должны исправить наверняка.

+0

Спасибо за ответ. Между прочим, я не собирался публиковать поля. Просто привязали их к частным. –

Смежные вопросы