我已经创建了一个用于优化pdf文件的.net应用程序。实际上我必须优化许多文件,我已经调用了这样的线程:
CheckForIllegalCrossThreadCalls = false;
thOptimize = new Thread(csCommon.pdfFilesCompressAndMove);
thOptimize.Start();
我也发现了没有。处理器和核心使用此:
int processors=Environment.ProcessorCount
int coreCount = 0;
foreach (var item in new System.Management.ManagementObjectSearcher("Select * from Win32_Processor").Get())
{
coreCount += int.Parse(item["NumberOfCores"].ToString());
}
我在我的机器上找到了4个处理器和2个核心。
现在我的问题是我想对所有处理器使用函数pdfFilesCompressAndMove
,即我想同时优化多个文件。换句话说,我想让所有处理器忙于优化。
请指导我怎么可能?
答案 0 :(得分:1)
您想要的是生产者/消费者队列。
这里发生的是生产者创建供消费者处理的工作项。当生产者可以比消费者处理消费者更快的速度为消费者创造工作时,这种方法很有效。然后,您有一个或多个消费者处理此工作队列。
这是我用于此类事情的生产者消费者类:
public class ProducerConsumer<T>:IDisposable
{
private int _consumerThreads;
private readonly Queue<T> _queue = new Queue<T>();
private readonly object _queueLocker = new object();
private readonly AutoResetEvent _queueWaitHandle = new AutoResetEvent(false);
private readonly Action<T> _consumerAction;
private readonly log4net.ILog _log4NetLogger = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
private bool _isProcessing = true;
public ProducerConsumer(Action<T> consumerAction,int consumerThreads,bool isStarted)
{
_consumerThreads = consumerThreads;
if (consumerAction == null)
{
throw new ArgumentNullException("consumerAction");
}
_consumerAction = consumerAction;
if (isStarted)
Start();
//just in case the config item is missing or is set to 0. We don't want to have the queue build up
}
public ProducerConsumer(Action<T> consumerAction, int consumerThreads):this(consumerAction,consumerThreads,true)
{
}
public void Dispose()
{
_isProcessing = false;
lock(_queueLocker)
{
_queue.Clear();
}
}
public void Start()
{
if (_consumerThreads == 0)
_consumerThreads = 2;
for (var loop = 0; loop < _consumerThreads; loop++)
ThreadPool.QueueUserWorkItem(ConsumeItems);
}
public void Enqueue(T item)
{
lock (_queueLocker)
{
_queue.Enqueue(item);
// After enqueuing the item, signal the consumer thread.
_queueWaitHandle.Set();
}
}
private void ConsumeItems(object state)
{
while (_isProcessing)
{
try
{
var nextItem = default(T);
bool doesItemExist;
lock (_queueLocker)
{
int queueCount = _queue.Count;
doesItemExist = queueCount > 0;
if (doesItemExist)
{
nextItem = _queue.Dequeue();
}
if (queueCount > 0 && queueCount % 50 == 0)
_log4NetLogger.Warn(String.Format("Queue is/has been growing. Queue size now:{0}",
queueCount));
}
if (doesItemExist)
{
_consumerAction(nextItem);
}
else
{
_queueWaitHandle.WaitOne();
}
}
catch (Exception ex)
{
_log4NetLogger.Error(ex);
}
}
}
}
这是一个泛型类,因此T是您要处理的对象类型。您还为它提供了一个Action,它是执行实际处理的方法。这应该允许您以干净的方式一次处理多个PDF文件。
答案 1 :(得分:0)
检查此主题:Optimal number of threads per core
如果您的线程方法csCommon.pdfFilesCompressAndMove
非常耗费CPU(我可以通过其名称猜测),那么每个核心应该启动1个线程。您最好使用ThreadPool.QueueUserWorkItem
,而不是手动创建线程,它将负责在核心之间生成线程。
在你的情况下,据我所知,你有8个内核,所以你可以调用ThreadPool.QueueUserWorkItem(csCommon.pdfFilesCompressAndMove)
8次,并在你的一个线程完成时再次调用,保持运行线程的总数等于8。
答案 2 :(得分:0)
我会使用ThreadPool
,因为据我所知,它由.NET Framework和操作系统管理,总是为目标系统创建最佳线程数。
答案 3 :(得分:0)
我认为您最好的选择是从简单的事情开始,这样您就可以了解问题的性能特征。
List<string> items = GetListOfPdfFilesToProcess();
int numCores = 4;
int maxListChunkSize = (int)Math.Ceiling(items.Count / (double)numCores);
ManualResetEvent[] events = new ManualResetEvent[numCores];
for (int i = 0; i < numCores; i++)
{
ThreadPool.QueueUserWorkItem(ProcessFiles, new object[]
{
items.Skip(i * maxListChunkSize).Take(maxListChunkSize).ToList(), events[i]
});
}
WaitHandle.WaitAll(events);
....
private static void ProcessFiles(object state)
{
object[] stateArray = (object[])state;
List<string> filePaths = (List<string>)stateArray[0];
ManualResetEvent completeEvent = (ManualResetEvent)stateArray[1];
for (int i = 0; i < filePaths.Count; i++)
{
csCommon.pdfFilesCompressAndMove(your parameters);
}
completeEvent.Set();
}
这里最重要的是将工作分成numCores
个块。通过这种方式,您应该能够充分利用所有CPU内核,但保留一个非常简单的编程模型。
请记住,这不会进行任何错误处理 - 您需要处理此问题。如果csCommon.pdfFilesCompressAndMove
无法处理文件,也可以考虑做些什么。最简单的方法是记录错误并稍后检查,但如果您认为下次再次成功,可以尝试重新处理该文件。
您会注意到state
对象只是一个数组;如果您需要将大量参数传递给ProcessFiles
,那么将这些参数包装到单个对象中并将其作为state
传递可能更简单。
修改强>
从Tick
事件中使用:
private void TimerTick(object sender, EventArgs e)
{
//Disabling the timer will ensure the `TimerTick` method will not try to run
//while we are processing the files. This covers the case where processing takes
//longer than 2 minutes.
timer.Enabled = false;
//Run the first block of code in my answer.
//Reenabling the timer will start the polling back up.
timer.Enabled = true;
}
我还建议检查您必须处理的文件数:如果没有,请重新启用计时器并返回。这将避免排队一堆实际上没有做任何事情的操作。