在我正在开发的应用程序中,我将使用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的阶段,我得到一个异常。类似于“对象同步方法是从一个不同步的代码块中调用的”。有没有办法做到这一点?谢谢你的帮助
答案 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
。
代码中可能还有其他问题......我没有进行彻底的审核。但上面你需要确定。