在排队方式执行cpu绑定作业时避免线程池饥饿的策略

时间:2012-12-11 13:15:59

标签: c# .net asp.net-mvc multithreading thread-safety

我的目标是避免使用线程池线程进行CPU绑定工作,从而避免IIS停止响应新请求的情况。

你能看到下面的代码出现任何问题吗?这是一种安全/干净的方法吗?你能提供任何改进吗?

    private static ConcurrentQueue<Job> Jobs = new ConcurrentQueue<Job>();
    static int threadCount = 0;

    private void QueueJob(Job job)
    {

        lock(Jobs)
        {
            Jobs.Enqueue(job);
            if (threadCount == 0)
            {

                Interlocked.Increment(ref threadCount); 
                var t= new Thread(new ThreadStart(ConsumeQueue));              
                t.Start();

            }
        }



    }
    private void ConsumeQueue()
    {
        while (true)
        {
            lock (Jobs)
            { 
                if (!Jobs.Any())
                {
                    Interlocked.Decrement(ref threadCount);
                    return;
                }
            }

            Job j;

            var jobToDo = Jobs.TryDequeue(out j);

            if (jobToDo)
            {
                DoCPUBoundWork(j);
            }
        }

    }

2 个答案:

答案 0 :(得分:2)

这是一个满足您需求的基本队列:

//sealed so we don't have to implement full IDisposable pattern
sealed class Q:IDisposable
{
    private CancellationTokenSource cts = new CancellationTokenSource();
    private BlockingCollection<Action> queue =
        new BlockingCollection<Action>(new ConcurrentQueue<Action>());

    public Q()
    {
        new Thread(() => RunQueue()).Start();
    }

    private void RunQueue()
    {
        while(!cts.IsCancellationRequested)
        {
            Action action;
            try
            {
                //lovely... blocks until something is available
                //so we don't need to deal with messy synchronization
                action = queue.Take(cts.Token); 
            }
            catch(OperationCanceledException)
            {
                break;
            }
            action();
        }
    }

    public void AddJob(Action action)
    {
        try
        {
            queue.Add(action,cts.Token);
        }
        catch(OperationCanceledException e)
        {
            throw new ObjectDisposedException("Q is disposed",e);
        }
    }

    public void Dispose()
    {
        if(!cts.IsCancellationRequested)
        {
            cts.Cancel();
        }
    }
}

使用方法如下:

Q actionQueue=new Q();
actionQueue.AddJob(() => Console.WriteLine("action1"));
actionQueue.AddJob(() => Console.WriteLine("action2"));
actionQueue.AddJob(() => Console.WriteLine("action3"));

答案 1 :(得分:1)

您的线程可以在Enqueue作业

之前终止
lock (Jobs)
{ 
     if (!ResizeJobs.Any())
     {
         Interlocked.Decrement(ref threadCount);
         return;
     }
}

此后,另一份工作将执行     Jobs.Enqueue(作业);

我认为您不需要终止工作线程。它应该等待睡眠状态下的工作