我可以从C#中的其他类调用Monitor.Pulse吗?

时间:2014-12-16 22:18:23

标签: c# multithreading thread-synchronization

在我正在开发的应用程序中,我将使用2个线程来执行各种操作。 (我不会在这里详细说明。)这些线程在循环中工作,检查是否有工作要做,做功,计算他们需要等待和等待的时间。 (见下文)

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);
         }

      }
  }

正如您所看到的,这两个线程是在asax文件的Application_Start方法中启动的。如果有可用的东西,它们会运行,然后计算他们需要等待的时间段然后等待。但是,当Web应用程序的用户执行操作时,新记录将被插入到数据库中,并且在某些情况下,两个线程中的任何一个都必须比计划更早地恢复操作。所以,假设我的DataAccess类中有一个方法,它会在数据库中插入新数据。 (见下文)

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

看起来这是一个无效的操作,因为在从InsertNewAuction方法调用Monitor.Pulse的阶段,我得到一个异常。类似于“对象同步方法是从一个不同步的代码块中调用的”。有没有办法做到这一点?谢谢你的帮助

2 个答案:

答案 0 :(得分:2)

至于您所看到的具体错误,这是因为必须在Monitor锁中调用Monitor.Pulse,就像这样(我使用的是锁而不是Enter / Exit,因为它更安全,可以确保锁总是被释放,因为它使用了正确的try / finally块):

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

关于这里更一般的设计问题,将锁定对象公开为公共(甚至更糟糕的全局)字段通常是危险的。特别是,当以不同的顺序公开和获取多个全局锁时,或者在您持有锁时阻止调度到UI线程的情况时,它可能是死锁的处方。考虑寻找其他方法来完成你所追求的目标。

答案 1 :(得分:0)

如另一个答案中所述,您必须获取锁定才能在监视器对象上调用Monitor.Pulse()

也就是说,您的代码至少还有一个严重的错误:您没有以线程安全的方式初始化同步对象,这很容易导致两个不同的线程使用两个不同的对象实例,导致这些不同步线程:

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

如果两个线程同时调用此getter,则每个线程可能会将o1视为null并尝试初始化它。然后每个都可能为对象实例返回不同的值。

您只需在初始化程序中初始化对象:

private static readonly object o1 = new object();

然后从getter返回它:

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

解决了线程安全问题。但是你仍然有代码的其他问题。首先,您应该在对象中封装同步,而不是公开实际的同步object实例。其次,假设您要公开同步对象,我不明白您为什么要打扰该属性,因为您将该字段公之于众。如果你想使用一个属性,那么该字段应该是私有的。

如果属性遵循正常的.NET命名约定,那也会更好。返回对象的方法将具有" Get"在名称中,但财产不会。只需将其命名为" FirstObjectInstance"。

同样如Dan所述,请在您想要获取锁定的任何地方使用lock

代码中可能还有其他问题......我没有进行彻底的审核。但上面你需要确定。