How to limit CPU usage of a process

I don't know the exact command for C#, but it would be a command that gives control back to the operating system. I think in C++ it might be a delay or sleep that will do that. So the equivalent call in C# that will delay the code and give cycles back to the OS.


If the program you want to limit is not yours, there are several options:

  • set the process priority as Idle and do not limit the CPU usage as the CPU should be used as much as possible in any case. It's OK to have your CPU running 100% all the time if there is something useful to do. If the priority is idle, then the CPU usage of this particular process will be reduced if another program requires CPU.
  • if your system is multi-core or multi-cpu, then you might want to set the processor affinity. This will tell your program to use only the processor(s) you want him to use. For example, if your program is multithreaded and able to consume 100% of your two CPUs, then set his affinity to only use one CPU. His usage will then be 50% only.
  • Worst option but actually used by 90% of the "CPU limiter programs" you'll find on the web: measure the CPU usage of a process and Suspend and Resume it regularly until it's CPU usage goes to the value you want.

To suspend/resume a process that is not yours, you'll have to use P/Invoke (and this requires to have access to the process, so if you are Windows Vista or above, take care of UAC for admin rights):

/// <summary>
/// The process-specific access rights.
/// </summary>
[Flags]
public enum ProcessAccess : uint
{
    /// <summary>
    /// Required to terminate a process using TerminateProcess.
    /// </summary>
    Terminate = 0x1,

    /// <summary>
    /// Required to create a thread.
    /// </summary>
    CreateThread = 0x2,

    /// <summary>
    /// Undocumented.
    /// </summary>
    SetSessionId = 0x4,

    /// <summary>
    /// Required to perform an operation on the address space of a process (see VirtualProtectEx and WriteProcessMemory).
    /// </summary>
    VmOperation = 0x8,

    /// <summary>
    /// Required to read memory in a process using ReadProcessMemory.
    /// </summary>
    VmRead = 0x10,

    /// <summary>
    /// Required to write to memory in a process using WriteProcessMemory.
    /// </summary>
    VmWrite = 0x20,

    /// <summary>
    /// Required to duplicate a handle using DuplicateHandle.
    /// </summary>
    DupHandle = 0x40,

    /// <summary>
    /// Required to create a process.
    /// </summary>
    CreateProcess = 0x80,

    /// <summary>
    /// Required to set memory limits using SetProcessWorkingSetSize.
    /// </summary>
    SetQuota = 0x100,

    /// <summary>
    /// Required to set certain information about a process, such as its priority class (see SetPriorityClass).
    /// </summary>
    SetInformation = 0x200,

    /// <summary>
    /// Required to retrieve certain information about a process, such as its token, exit code, and priority class (see OpenProcessToken, GetExitCodeProcess, GetPriorityClass, and IsProcessInJob).
    /// </summary>
    QueryInformation = 0x400,

    /// <summary>
    /// Undocumented.
    /// </summary>
    SetPort = 0x800,

    /// <summary>
    /// Required to suspend or resume a process.
    /// </summary>
    SuspendResume = 0x800,

    /// <summary>
    /// Required to retrieve certain information about a process (see QueryFullProcessImageName). A handle that has the PROCESS_QUERY_INFORMATION access right is automatically granted PROCESS_QUERY_LIMITED_INFORMATION.
    /// </summary>
    QueryLimitedInformation = 0x1000,

    /// <summary>
    /// Required to wait for the process to terminate using the wait functions.
    /// </summary>
    Synchronize = 0x100000
}

[DllImport("ntdll.dll")]
internal static extern uint NtResumeProcess([In] IntPtr processHandle);

[DllImport("ntdll.dll")]
internal static extern uint NtSuspendProcess([In] IntPtr processHandle);

[DllImport("kernel32.dll", SetLastError = true)]
internal static extern IntPtr OpenProcess(
    ProcessAccess desiredAccess,
    bool inheritHandle,
    int processId);

[DllImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern bool CloseHandle([In] IntPtr handle);

public static void SuspendProcess(int processId)
{
    IntPtr hProc = IntPtr.Zero;
    try
    {
        // Gets the handle to the Process
        hProc = OpenProcess(ProcessAccess.SuspendResume, false, processId);

        if (hProc != IntPtr.Zero)
        {
            NtSuspendProcess(hProc);
        }
    }
    finally
    {
        // Don't forget to close handle you created.
        if (hProc != IntPtr.Zero)
        {
            CloseHandle(hProc);
        }
    }
}

Tags:

C#