[.NET] Suspend Process 暫停批次執行

Mainframe主機環境中當批次執行太久,想釋放CPU資源給其他批次執行時,我們會調整較低等級的Priority或者將這支JOB Cancel,最近搜尋了在開放系統.NET環境的作法。

筆記一下需求及解決方案:

由外部Process發出

  • 暫停批次
  • 被暫停的批次繼續執行
  • 取消批次

 

1.Process vs Thread,先了解處理程序與執行序基本概念,剛好批次都是一個執行檔,執行時就一個Process,然後視程式功能以單執行序或多執行序執行。

https://en.wikipedia.org/wiki/Thread_(computing)

2.Process內建的功能: 具備kill() 砍job 取消批次功能,但少了臨時暫停 和 暫停工作繼續執行

不過thread 執行序方法則有

如果要砍job,kill()很適合;但要暫停手邊的工作,待會再繼續執行,看起來要從thread這一層下手,但MSDN中的方法被標示為過時,原因是擔心thread lock,但我們還是希望可以有這個功能,繼續匍匐前進。

3.現成增加Process Extension解決方案: http://stackoverflow.com/questions/71257/suspend-process-in-c-sharp

果然在stackoverflow找到答案,利用呼叫Kernel32.lib中的SuspendThreadResumeThread功能來解決

using System;
using System.Diagnostics;
using System.Runtime.InteropServices;


public static class ProcessExtension
{
    [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);

    public static void Suspend(this Process process)
    {
        foreach (ProcessThread thread in process.Threads)
        {
            var pOpenThread = OpenThread(ThreadAccess.SUSPEND_RESUME, false, (uint)thread.Id);
            if (pOpenThread == IntPtr.Zero)
            {
                break;
            }
            SuspendThread(pOpenThread);
        }
    }
    public static void Resume(this Process process)
    {
        foreach (ProcessThread thread in process.Threads)
        {
            var pOpenThread = OpenThread(ThreadAccess.SUSPEND_RESUME, false, (uint)thread.Id);
            if (pOpenThread == IntPtr.Zero)
            {
                break;
            }
            ResumeThread(pOpenThread);
        }
    }
    public static void Print(this Process process)
    {
        Console.WriteLine("{0,8}    {1}", process.Id, process.ProcessName);
    }
}

[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)
}

Win kernel api規格書上提到

suspend方法

If the function succeeds, execution of the specified thread is suspended and the thread's suspend count is incremented.
Suspending a thread causes the thread to stop executing user-mode (application) code.

Resume方法

The ResumeThread function decrements a thread's suspend count. When the suspend count is decremented to zero,
the execution of the thread is resumed.

加上print方法

顯示process id及process name

4.撰寫一個輸入"PID"和"處理選項"的方法來套

/// <summary>
/// Process option 
/// </summary>
public enum ProcessEnum
{
    Suspend = 0, Resume, Print, Kill
}
/// <summary>
/// Control the process
/// </summary>
/// <param name="PID">The pid.</param>
/// <param name="OPTION">The option.</param>
/// <returns></returns>
public string ctrlP(int PID, ProcessEnum OPTION)
{
    try
    {
        var p = Process.GetProcessById(PID);
        if (p != null)
        {
            switch (OPTION)
            {     
                case ProcessEnum.Suspend:
                    p.Suspend();
                    break;
                case ProcessEnum.Resume:
                    p.Resume();
                    break;
                case ProcessEnum.Print:
                    p.Print();
                    break;
                case ProcessEnum.Kill:
                    p.Kill();
                    break;
            }
            return string.Format("00:{0}({1})", PID, OPTION);
        }
        else
        {
            return string.Format("23:{0}({1})", PID, OPTION);
        }
    }
    catch (Exception e)
    {
        using (StreamWriter sw = File.AppendText(LogURL)) { sw.WriteLine("{0:u} Job Error:{1}({2}), {3}", DateTime.Now, PID, OPTION, e.ToString()); }
        return string.Format("99:{0}", e.ToString());
    }
}

 

5.執行中的批次程式 (狀態:執行中),他是1支每5秒寫1筆log的測試程式,總共要寫10次log

6.呼叫Suspend()後,狀態-(已暫止),從資料庫觀察,程式已經寫了3次log

呼叫Resume()後,批次也繼續將剩餘的7次log工作完成了,目前測試單執行序process 暫止都很正常。

之後有時間繼續來測試多執行序的process 或 改用powershell cmdlet操作 process