我即将启动一个创建作业调度程序的C#.NET 4.0项目。
我的计划是使用信号量,并将并发条目数设置为系统核心数。将为队列中的每个作业创建一个新线程,并且所有线程将在开头被信号量阻塞。
我的问题是当信号量调用release()方法时,保证高优先级线程首先进入信号量。可行?
我的第二个问题是当更高优先级的作业线程存在并让退出的作业线程返回到线程队列以等待信号量时,让信号量内的线程退出。可行?
对于这两个问题,信号量是正确的方法吗?如果不是你的建议么?
答案 0 :(得分:4)
好吧,我会更倾向于以下的事情......
首先,启动所需的所有线程:
for(int i=0; i < Environment.ProcessorCount; i++)
{
Thread t = new Thread(RunWork);
// setup thread
t.Start();
threads.Add(t);
}
您需要一个界面来描述任务的优先级
interface ITask {
PrioirtyType Prioirty { get; }
bool Complete { get; }
void PerformOneUnitOfWork();
}
然后创建一个Queue管理对象。这显然会变得更复杂,因为它可能需要与您的数据库等同步......
class MyQueue<TJob> where TJob : ITask
{
Queue<TJob> high, med, low;
bool GetNextJob(ref TJob work)
{
if(work.Priority == PriorityType.High && !work.Complete)
return true;
lock(this)
{
if(high.Count > 0)
{
Enqueue(work);//requeue to pick back up later
work = high.Dequeue();
return true;
}
if(work.Priority == PriorityType.Med && !work.Complete)
return true;
if(med.Count > 0)
{
Enqueue(work);//requeue to pick back up later
work = med.Dequeue();
return true;
}
if(!work.Complete)
return true;
if(low.Count > 0)
{
work = low.Dequeue();
return true;
}
work = null;
return false;
}
void Enqueue(TJob work)
{
if(work.Complete) return;
lock(this)
{
else if(work.Priority == PriorityType.High) high.Enqueue(work);
else if(work.Priority == PriorityType.Med) med.Enqueue(work);
else low.Enqueue(work);
}
}
}
最后创建您的工作线程,如下所示:
public void RunWork()
{
ITask job;
while(!_shutdown.WaitOne(0))
{
if(queue.GetNextJob(ref job))
job.PerformOneUnitOfWork();
else
WaitHandle.WaitAny(new WaitHandle[] { _shutdown, queue.WorkReadyHandle });
}
}
答案 1 :(得分:3)
对于这两个问题,信号量是正确的方法吗?如果不是你的建议么?
这取决于。通常,每个线程最好有多个作业,因为许多(特别是长时间运行的)工作项将花费时间等待除CPU之外的其他事情。例如,如果您正在从WCF服务或其他相关问题开始工作,您可能会花费大量时间来阻止和闲置。
在这种情况下,最好让您的工作按需安排。在这种情况下使用ThreadPool可能更好。
但是,如果作业都是高CPU,那么您的方法可能是理想的。优先级队列可用于跟踪调度优先级,并决定运行哪个作业。
话虽如此,我可能不会使用信号量。虽然它可以工作,但是单个计数器(通过Interlocked.Increment / Decrement管理)和ManualResetEvent也可以正常工作,并且重量更轻。
答案 2 :(得分:-1)
有一个很棒的框架从Java移植到C#检查出来