3

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すべてのプロセッサにこの関数を使用したい、つまり同時に複数のファイルを最適化したい、つまり、最適化ですべてのプロセッサをビジー状態にしておきたいということです。

どうすれば可能ですか?

4

4 に答える 4

1

必要なのはプロデューサー/コンシューマー キューです。

ここで何が起こるかというと、プロデューサーはコンシューマーが処理するワークアイテムを作成します。これは、コンシューマーが処理できるよりもはるかに速くプロデューサーがコンシューマーのために作業を作成できる場合にうまく機能します。次に、この作業キューを処理する 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 ファイルを一度にクリーンな方法で処理できるようになります。

于 2012-12-06T08:40:23.320 に答える
0

このスレッドを確認してください:コアあたりの最適なスレッド数

スレッドメソッドcsCommon.pdfFilesCompressAndMoveが非常にCPUを消費する場合(名前から推測できます)、コアごとに1つのスレッドを開始する必要があります。ThreadPool.QueueUserWorkItem手動でスレッドを作成するよりも、コア間のスレッドの生成を処理する方が適切です。あなたの場合、私が理解しているように、コアは8つあるのでThreadPool.QueueUserWorkItem(csCommon.pdfFilesCompressAndMove)、8回呼び出し、スレッドの1つが終了したときに再度呼び出すことができ、実行中のスレッドの総数は8になります。

于 2012-12-06T08:37:51.960 に答える
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それらのパラメーターを 1 つのオブジェクトにラップして、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;
}

また、処理する必要があるファイルの数を確認することをお勧めします。何もない場合は、タイマーを再度有効にして戻ります。これにより、実際には何もしない一連の操作がキューに入るのを回避できます。

于 2012-12-06T10:00:42.647 に答える
0

私がThreadPool知っている限りでは、.NET Framework と OS によって、ターゲット システムに最適な数のスレッドが常に作成されるように管理されているためです。

于 2012-12-06T08:58:35.943 に答える