web-dev-qa-db-ja.com

プログラムでプログラムのCPU使用率を70%未満に制限するにはどうすればよいですか?

最近、プログラムを構築するときに健康志向が高まっています。ほとんどのプログラムの実行には2〜3分かかることがわかりました。また、タスクスケジューラを確認すると、CPU使用率が100%消費されていることがわかります。プログラムでこの使用をコードで制限しますか?これにより、特定の時間に複数のプログラムを実行できるようになります。

おかげで、ニディ

45
Nidhi

それはあなたの関心事ではありません...実行中のプロセス間でプロセッサ時間を分散するのはオペレーティングシステムの仕事です。他のプロセスに最初にそれらの処理を完了させるきっかけを与えたい場合は、 Process.PriorityClass 値。

参照: Windowsの「Nice」に相当

29
Shog9

このスレッドは4年以上前のものであり、受け入れられた回答が質問に答えるのではなく、批判するのは依然として不愉快です。プログラムでかかるCPU時間を制限したい正当な理由はたくさんあります。頭の中でいくつか挙げておきます。

利用可能なすべての空きCPUサイクルを使用しないのは無駄に思えるかもしれませんが、この考え方には欠陥があります。古いCPUとは異なり、最新のCPUのほとんどは固定クロック速度で実行されません。多くのCPUには、負荷が低いときにクロック速度とCPU電圧を下げる省電力モードがあります。 CPUは、計算を実行するときに、NOOPを実行するよりも多くの電力を消費します。これは特に、高負荷時にCPUを冷却するためにファンを必要とするラップトップに関連します。 100%で短時間タスクを実行すると、25%で4倍の時間タスクを実行するよりもはるかに多くのエネルギーを使用できます。

バックグラウンドで定期的にファイルのインデックスを作成するように設計されたバックグラウンドタスクを作成しているとします。インデックス作成タスクは、より低い優先度でできる限り多くのCPUを使用する必要がありますか、それとも自分自身を25%に絞り込み、必要なだけ長くかかる必要がありますか?ラップトップでCPUを100%消費すると、CPUが熱くなり、ファンが作動し、バッテリーがかなり早く消耗し、ユーザーが迷惑になります。インデックスサービス自体が抑制されている場合、ラップトップは非常に低いCPUクロック速度と電圧で完全に受動的な冷却で実行できる可能性があります。

ちなみに、Windowsインデックスサービスは、以前のバージョンでは実行できなかった新しいバージョンのWindowsで自動調整されるようになりました。それでもスロットルを調整せず、頻繁に人々を困らせるサービスの例については、Windowsインストーラモジュールを参照してください。

アプリケーションの一部をC#で内部的に調整する方法の例:

public void ThrottledLoop(Action action, int cpuPercentageLimit) {
    Stopwatch stopwatch = new Stopwatch();

    while(true) {
        stopwatch.Reset();
        stopwatch.Start();

        long actionStart = stopwatch.ElapsedTicks;
        action.Invoke();
        long actionEnd = stopwatch.ElapsedTicks;
        long actionDuration = actionEnd - actionStart;

        long relativeWaitTime = (int)(
            (1/(double)cpuPercentageLimit) * actionDuration);

        Thread.Sleep((int)((relativeWaitTime / (double)Stopwatch.Frequency) * 1000));
    }
}
87
Ryan

まず第一に、質問は完全に有効であり、スレッドの優先順位がまったく十分でない場合があるというライアンに同意します。他の答えは非常に理論的であり、アプリケーションが適切に設計されていてもスロットルする必要がある状況では実用的ではありません。 Ryanは、比較的短いタスクが高い頻度で実行される場合の簡単なソリューションを提供します。ただし、タスクにvery長い時間(たとえば、1分程度)がかかり、それを壊したくない、またはしたくない場合がありますその間でスロットリングを実行できる小さなチャンクに分割します。これらのケースでは、次の解決策が役立つ場合があります。

ビジネスコードにスロットルを実装するのではなく、Steam全体で動作するようにアルゴリズム自体を設計し、「外部から」操作を実行するスレッドをスロットルするだけで済みます。一般的なアプローチはRyanの回答と同じです。現在の使用状況に基づいて一時停止時間を計算し、この時間間隔の間スレッドを一時停止してから再開します。スロットルしたいプロセスを考えると、これは論理です:

public static class ProcessManager
{
    [Flags]
    public enum ThreadAccess : int
    {
        TERMINATE = (0x0001),
        SUSPEND_RESUME = (0x0002),
        GET_CONTEXT = (0x0008),
        SET_CONTEXT = (0x0010),
        SET_INFORMATION = (0x0020),
        QUERY_INFORMATION = (0x0040),
        SET_THREAD_TOKEN = (0x0080),
        IMPERSONATE = (0x0100),
        DIRECT_IMPERSONATION = (0x0200)
    }

    [DllImport("kernel32.dll")]
    static extern IntPtr OpenThread(ThreadAccess dwDesiredAccess, bool bInheritHandle, uint dwThreadId);

    [DllImport("kernel32.dll")]
    static extern uint SuspendThread(IntPtr hThread);

    [DllImport("kernel32.dll")]
    static extern int ResumeThread(IntPtr hThread);

    [DllImport("kernel32.dll")]
    static extern int CloseHandle(IntPtr hThread);

    public static void ThrottleProcess(int processId, double limit)
    {
        var process = Process.GetProcessById(processId);
        var processName = process.ProcessName;
        var p = new PerformanceCounter("Process", "% Processor Time", processName);
        while (true)
        {
            var interval = 100;
            Thread.Sleep(interval);

            var currentUsage = p.NextValue() / Environment.ProcessorCount;
            if (currentUsage < limit) continue;
            var suspensionTime = (currentUsage-limit) / currentUsage * interval;
            SuspendProcess(processId);
            Thread.Sleep((int)suspensionTime);
            ResumeProcess(processId);
        }
    }

    private static void SuspendProcess(int pid)
    {
        var process = Process.GetProcessById(pid);

        if (process.ProcessName == string.Empty)
            return;

        foreach (ProcessThread pT in process.Threads)
        {
            IntPtr pOpenThread = OpenThread(ThreadAccess.SUSPEND_RESUME, false, (uint)pT.Id);

            if (pOpenThread == IntPtr.Zero)
            {
                continue;
            }

            SuspendThread(pOpenThread);

            CloseHandle(pOpenThread);
        }
    }

    private static void ResumeProcess(int pid)
    {
        var process = Process.GetProcessById(pid);

        if (process.ProcessName == string.Empty)
            return;

        foreach (ProcessThread pT in process.Threads)
        {
            IntPtr pOpenThread = OpenThread(ThreadAccess.SUSPEND_RESUME, false, (uint)pT.Id);

            if (pOpenThread == IntPtr.Zero)
            {
                continue;
            }

            var suspendCount = 0;

            do
            {
                suspendCount = ResumeThread(pOpenThread);
            } while (suspendCount > 0);

            CloseHandle(pOpenThread);
        }
    }
}

このソリューションの利点は、チェック間隔が「長時間実行タスク」の期間とは無関係になることです。さらに、ビジネスロジックとスロットルロジックは分離されています。サスペンス/再開コードは this thread から着想を得ています。スロットリングの破棄と終了は上記のソリューションで実装する必要があることに注意してください。これは製品コードではありません。

21
Marc

CPUの使用を抑制するGovernorクラスを作成できます。このクラスには、CPUバインド関数によって定期的に呼び出す必要があるユーティリティメソッド(たとえば、関数のwhileループ内でこのユーティリティ関数を呼び出す)が含まれます。ガバナーは、経過時間が特定のしきい値を超えたかどうかを確認し、すべてのCPUを消費しないように一定期間スリープします。

これは単純なJava実装)で、シングルスレッドのCPUバインド関数がある場合にCPU使用率が50%に抑制されます(アイデアを理解するためです)。

public class Governor
{
  long start_time;

  public Governor()
  {
    this.start_time = System.currentTimeMillis();
  }

  public void throttle()
  {
    long time_elapsed = System.currentTimeMillis() - this.start_time;

    if (time_elapsed > 100) //throttle whenever at least a 100 millis of work has been done
    {
      try { Thread.sleep(time_elapsed); } catch (InterruptedExceptione ie) {} //sleep the same amount of time

      this.start_time = System.currentTimeMillis(); //reset after sleeping.
    }
  }
}

CPUバインド関数はGovernorをインスタンス化し、関数内で定期的にthrottleを呼び出すだけです。

15
mal

皆様、お返事ありがとうございます。私はこれと数時間実行されるexeに取り組んでおり、他の人を助けるために共有したいと考えています。データを暗号化してクラウドにプッシュするWPFアプリで設定して忘れるクラスを作成しましたが、WPFアプリのタイミングやWPFアプリに必要なものに干渉することはできませんでしたリソースの途中で、WPFアプリがリソース消費が最も高い状態にあるときに無効にするフラグも追加します。このWPFはTPLですでに高度にスレッド化されています。このソリューションには、プロセスの優先順位セットの両方があります

myProcess.PriorityClass = ProcessPriorityClass.Idle;

cPUの使用率は制限されています。

次に、mainDisplay.xaml.csで使用します

ProcessManagement.StartProcess(5);

mainWindow()

そして、そのexeが実行されているときにポップアップウィンドウはありません

RedirectStandardOutput = true,  
UseShellExecute = false,
CreateNoWindow = true

オブジェクト初期化子

internal class ProcessManagement
{
    private static int CpuPercentageLimit { get; set; }

    public static void StartProcess(int cpuPercent)
    {
        CpuPercentageLimit = cpuPercent;
        var stopwatch = new Stopwatch();
        while (true)
        {
            stopwatch.Reset();
            stopwatch.Start();
            var actionStart = stopwatch.ElapsedTicks;
            try
            {
                var myProcess = new Process
                {
                    StartInfo =
                    {
                        FileName = @"D:\\Source\\ExeProgram\\ExeProgram\\bin\\Debug\\ExeProgram.exe",
                        RedirectStandardOutput = true,
                        UseShellExecute = false,
                        CreateNoWindow = true
                    }
                };
                myProcess.Start();
                myProcess.PriorityClass = ProcessPriorityClass.Idle;
                myProcess.Refresh();
                myProcess.WaitForExit();
                var actionEnd = stopwatch.ElapsedTicks;
                var actionDuration = actionEnd - actionStart;
                long relativeWaitTime = (int)((1 / (double)CpuPercentageLimit) * actionDuration);
                var sleepTime = (int)((relativeWaitTime / (double)Stopwatch.Frequency) * 1000);
                Thread.Sleep(sleepTime);
                myProcess.Close();
            }
            catch (Exception e)
            {
                // ignored
            }
        }
    }
}

私のアプリケーションでは、24/7/365のように、何千もの画像を含む大量のデータをアップロードするのに十分な時間がありますが、UIを使用するときもアクティブのままにしておく必要があります。

4
Stephen Himes

マルチコアプロセッサを使用している場合は、各プロセスのアフィニティを設定して、使用するコアのみを使用できます。これは私が知っている最も近い方法です。ただし、デュアルコアでは50%、クアッドコアでは25%のパーセンテージのみを割り当てることができます。

4
Scott Hunt

threadpriority の低いスレッドでプログラムを実行できます。残りはオペレーティングシステム次第です。プロセスがCPUを100%消費することは悪くありません。私のSETIは通常、他のプログラムを邪魔することなく、残りのCPU時間をすべて使用しています。スレッドがより重要なプログラムよりも優先されるときにのみ問題が発生します。

3
Carra

[〜#〜] msdn [〜#〜] によると、スレッドの優先度のみを設定できます。

var t1 = new Thread(() => doSomething());
t1.Priority = ThreadPriority.BelowNormal;
t1.Start();

doSomethingは、theadを作成する関数です。優先度は、ThreadPriority列挙型メンバーの1つにすることができますLowest, BelowNormal, Normal, AboveNormal, Highest-説明については、上記のMSDNリンクを参照してください。優先度Normalがデフォルトです。

CPU使用率は、コアおよび論理プロセッサーお使いの物理CPU持っている *) -スレッドとプロセスがこれらのコアにどのように割り当てられるか(専用プロセッサーへの割り当ては "プロセッサーアフィニティー"と呼ばれます-詳細を知りたい場合それについて、 このStackOverflowの質問を参照してください )。


*) それを見つけるには、タスクマネージャーを開きます( Ctrl+Alt+Delete -[タスクマネージャ]を選択し、[パフォーマンス]に移動してCPUを選択します。使用率グラフの下に、[コア]と[論理プロセッサ]が表示されます。
コアはCPUに組み込まれた物理的なユニットですが、論理プロセッサは単なる抽象概念です。つまり、CPUで構成されるコアが多いほど、並列タスクをより速く処理できます。

3
Matt

あなたがコードを実行している場合、それは100%です

私はいくつかの睡眠で滑ることが影響を与えるかもしれないと思います。

その2〜3分の数字について疑問に思う必要があります。私も見たことがありますが、おそらく本当に必要ないものをたくさんロードして初期化していると思います。

2
Mike Dunlavey

私は正直に言って、アプリによるCPU使用率を制限しようとするのではなく、アプリケーションのプロファイリングにもっと力を入れて、存在する可能性のあるボトルネックと非効率性を明らかにして修正する必要があります。

1
itsmatt