在开展一个大型项目的过程中,我意识到我将来会安排很多电话。由于这些重量相当轻,我认为使用单独的调度程序可能会更好。
ThreadPool.QueueUserWorkItem (() =>
{
Thread.Sleep (5000);
Foo (); // Call is to be executed after sometime
});
所以我创建了一个单独的调度程序类,它在自己的线程上运行并执行这些事件。我有2个函数从不同的线程访问共享队列。我使用了一个锁,但由于其中一个线程需要休眠等待,我不知道如何释放锁。
class Scheduler
{
SortedDictionary <DateTime, Action> _queue;
EventWaitHandle _sync;
// Runs on its own thread
void Run ()
{
while (true)
{
// Calculate time till first event
// If queue empty, use pre-defined value
TimeSpan timeDiff = _queue.First().Key - DateTime.Now;
// Execute action if in the next 100ms
if (timeDiff < 100ms)
...
// Wait on event handle for time
else
_sync.WaitOne (timeDiff);
}
}
// Can be called by any thread
void ScheduleEvent (Action action, DataTime time)
{
_queue.Add (time, action);
// Signal thread to wake up and check again
_sync.Set ();
}
}
答案 0 :(得分:3)
问题很容易解决,请确保WaitOne在锁定之外。
//untested
while (true)
{
Action doit = null;
// Calculate time till first event
// If queue empty, use pre-defined value
lock(_queueLock)
{
TimeSpan timeDiff = _queue.First().Key - DateTime.Now;
if (timeDiff < 100ms)
doit = _queue.Dequeue();
}
if (doit != null)
// execute it
else
_sync.WaitOne (timeDiff);
}
_queueLock是一个私有帮助器对象。
答案 1 :(得分:3)
好的,用Monitor / Pulse取2。
void Run ()
{
while (true)
{
Action doit = null;
lock(_queueLock)
{
while (_queue.IsEmpty())
Monitor.Wait(_queueLock);
TimeSpan timeDiff = _queue.First().Key - DateTime.Now;
if (timeDiff < 100ms)
doit = _queue.Dequeue();
}
if (doit != null)
; //execute doit
else
_sync.WaitOne (timeDiff);
}
}
void ScheduleEvent (Action action, DataTime time)
{
lock (_queueLock)
{
_queue.Add(time, action);
// Signal thread to wake up and check again
_sync.Set ();
if (_queue.Count == 1)
Monitor.Pulse(_queuLock);
}
}
答案 2 :(得分:2)
由于您的目标是在特定时间段之后安排任务,为什么不使用System.Threading.Timer?它不需要为调度专用线程,并利用操作系统唤醒工作线程。我已经使用了这个(删除了一些注释和其他计时器服务功能):
public sealed class TimerService : ITimerService
{
public void WhenElapsed(TimeSpan duration, Action callback)
{
if (callback == null) throw new ArgumentNullException("callback");
//Set up state to allow cleanup after timer completes
var timerState = new TimerState(callback);
var timer = new Timer(OnTimerElapsed, timerState, Timeout.Infinite, Timeout.Infinite);
timerState.Timer = timer;
//Start the timer
timer.Change((int) duration.TotalMilliseconds, Timeout.Infinite);
}
private void OnTimerElapsed(Object state)
{
var timerState = (TimerState)state;
timerState.Timer.Dispose();
timerState.Callback();
}
private class TimerState
{
public Timer Timer { get; set; }
public Action Callback { get; private set; }
public TimerState(Action callback)
{
Callback = callback;
}
}
}
答案 3 :(得分:1)
monitores是针对这种情况而创建的,简单的问题可能会使应用程序变得费力,我提出了这个非常简单的解决方案,如果你想让关闭易于实现:
void Run()
{
while(true)
lock(this)
{
int timeToSleep = getTimeToSleep() //check your list and return a value
if(timeToSleep <= 100)
action...
else
{
int currTime = Datetime.Now;
int currCount = yourList.Count;
try{
do{
Monitor.Wait(this,timeToSleep);
if(Datetime.now >= (tomeToSleep + currtime))
break; //time passed
else if(yourList.Count != currCount)
break; //new element added go check it
currTime = Datetime.Now;
}while(true);
}
}catch(ThreadInterruptedException e)
{
//do cleanup code or check for shutdown notification
}
}
}
}
void ScheduleEvent (Action action, DataTime time)
{
lock(this)
{
yourlist.add ...
Monitor.Pulse(this);
} }