启动一个进程,如果其中一个线程正在占用CPU,则暂停它

时间:2013-11-18 01:36:59

标签: c# .net visual-c++ process skype

有一个well-known problem,Windows 8上的Skype占用了一些用户PC上100%的CPU核心。包括我的!有一个workaround courtesy of techfreak in Skype Community

  
      
  1. 下载并运行最新版本的进程资源管理器。 (http://download.sysinternals.com/files/ProcessExplorer.zip
  2.   
  3. 使用Skype在活动程序列表中搜索Skype.exe并双击它。
  4.   
  5. 转到线程选项卡并暂停或杀死IDLE时消耗最高资源的Skype线程。 (比如50%+ CPU)
  6.   

我每次重启后手动执行此操作都很烦恼,所以我想自动执行上述步骤,编写一个简单的C ++或C#“Skype启动程序”程序,执行以下操作:

  • 启动SKYPE.EXE
  • 每隔1秒唤醒一次,看看一个特定的Skype线程是否占用了过程中98%以上的CPU周期
  • 如果找到,则暂停该线程并退出启动程序进程
  • 否则循环最多10次,直到找到坏线程。

快速谷歌搜索后,我被Win32线程枚举API吓倒了,这个“查找并杀死/暂停邪恶线程”问题似乎相当普遍,所以我想知道是否有现有的样本我可以重新定位。有什么指针吗?

1 个答案:

答案 0 :(得分:1)

经过更多的谷歌搜索和一些死胡同与powershell(太多的安全麻烦,对新手太混乱)和WMI(比需要的更难),我终于找到了一个伟大的C# sample on MSDN Forums that will enumerate and suspend threads。这很容易适应在暂停罪魁祸首之前首先检查每个线程的CPU时间。

这是代码。只需编译并放入启动菜单,Skype将不再为您的办公室加热!

// code adapted from 
// http://social.msdn.microsoft.com/Forums/en-US/d51efcf0-7653-403e-95b6-bf5fb97bf16c/suspend-thread-of-a-process

using System;
using System.Linq;
using System.Threading.Tasks;
using System.Runtime.InteropServices;
using System.Diagnostics;
using System.Threading;
using System.ComponentModel;

namespace SkypeLauncher
{
    class Program
    {
        static void Main(string[] args)
        {
            Process[] procs = Process.GetProcessesByName("skype");
            if (procs.Length == 0)
            {
                Console.WriteLine("Skype not loaded. Launching. ");
                Process.Start(Environment.ExpandEnvironmentVariables(@"%PROGRAMFILES(X86)%\Skype\Phone\Skype.exe"));
                Thread.Sleep(8000); // wait to allow skype to start up & get into steady state
            }

            // wait to allow skype to start up & get into steady state, where "steady state" means
            // a lot of threads created
            Process proc = null;
            for (int i = 0; i < 50; i++)
            {
                procs = Process.GetProcessesByName("skype");
                if (procs != null)
                {
                    proc = procs[0];
                    if (proc.Threads.Count > 10)
                        break;
                }
                Thread.Sleep(1000); // wait to allow skype to start up & get into steady state
            }

            // try multiple times; if not hanging after a while, give up. It must not be hanging!
            for (int i = 0; i < 50; i++)    
            {
                // must reload process to get updated thread time info
                procs = Process.GetProcessesByName("skype");
                if (procs.Length == 0)
                {
                    Console.WriteLine("Skype not loaded. Exiting. ");
                    return;
                }
                proc = procs[0];

                // avoid case where exception thrown if thread is no longer around when looking at its CPU time, or
                // any other reason why we can't read the time
                var safeTotalProcessorTime = new Func<ProcessThread, double> (t => 
                    {
                        try { return t.TotalProcessorTime.TotalMilliseconds; }
                        catch (InvalidOperationException) { return 0; }
                    }
                );

                var threads = (from t in proc.Threads.OfType<ProcessThread>()
                                  orderby safeTotalProcessorTime(t) descending
                                  select new  
                                  {
                                      t.Id, 
                                      t.ThreadState, 
                                      TotalProcessorTime = safeTotalProcessorTime(t),  
                                  } 
                              ).ToList();
                var totalCpuMsecs = threads.Sum(t => t.TotalProcessorTime);
                var topThread = threads[0];
                var nextThread = threads[1];
                var topThreadCpuMsecs = topThread.TotalProcessorTime;
                var topThreadRatio = topThreadCpuMsecs / nextThread.TotalProcessorTime;

                // suspend skype thread that's taken a lot of CPU time and 
                // and it has lots more CPU than any other thread. 
                // in other words, it's been ill-behaved for a long time!
                // it's possible that this may sometimes suspend the wrong thread, 
                // but I haven't seen it break yet. 
                if (topThreadCpuMsecs > 10000 && topThreadRatio > 5)
                {
                    Console.WriteLine("{0} bad thread. {0:N0} msecs CPU, {1:N1}x CPU than next top thread.",
                        topThread.ThreadState == System.Diagnostics.ThreadState.Wait ? "Already suspended" : "Suspending",
                        topThreadCpuMsecs, 
                        topThreadRatio);
                    Thread.Sleep(1000);

                    IntPtr handle = IntPtr.Zero;
                    try
                    {
                        //Get the thread handle & suspend the thread
                        handle = OpenThread(2, false, topThread.Id);
                        var success = SuspendThread(handle);
                        if (success == -1)
                        {
                            Win32Exception ex = new Win32Exception(Marshal.GetLastWin32Error());
                            Console.WriteLine(ex.Message);
                        }
                        Console.WriteLine("Exiting");
                        Thread.Sleep(1000);
                        return;
                    }
                    finally
                    {
                        if (handle != IntPtr.Zero)
                            CloseHandle(handle);
                    };
                }

                Console.WriteLine("Top thread: {0:N0} msecs CPU, {1:N1}x CPU than next top thread. Waiting.",
                    topThreadCpuMsecs,
                    topThreadRatio);

                Thread.Sleep(2000); // wait between tries
            }
            Console.WriteLine("No skype thread is ill-behaved enough. Giving up.");
        }
        [DllImport("kernel32.dll", SetLastError = true)]
        private static extern int SuspendThread(IntPtr hThread);

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

        [DllImport("kernel32.dll", SetLastError = true)]
        private static extern
        IntPtr OpenThread(int dwDesiredAccess, [MarshalAs(UnmanagedType.Bool)]bool bInheritHandle, int dwThreadId);
    }
}