ما هي الطريقة الصحيحة لبرنامج لإنهاء عمليته (Windows)

StackOverflow https://stackoverflow.com/questions/1200507

سؤال

ج# .NET 3.5

لدي تطبيق وحدة تحكم يتم استدعاؤه بواسطة تطبيق آخر على الكمبيوتر. يعمل تطبيق وحدة التحكم هذا بشكل مستمر ، ويستمع للحصول على بيانات على stdin من عملية "الوالد".

ومع ذلك ، عندما يتم إيقاف أو قتل الوالد ، يستمر تطبيق وحدة التحكم التي بدأها. في ظل الظروف العادية ، فإنه يجلس ويخلف في انتظار المدخلات من stdin ، باستخدام الحد الأدنى من الموارد. ومع ذلك ، بمجرد أن يختفي الوالد ، يرفع تطبيق وحدة التحكم هذا وحدة المعالجة المركزية ويجوع قلبه الذي يعمل عليه باستخدام ما يقرب من 100 ٪. يستمر هذا حتى أقتل العملية يدويًا.

من الناحية المثالية ، فإن الوالد الدعائي ينظف بعده ، خاصة وأن هذا يحدث في ظل ظروف "إيقاف" العادية (غير الاستثنائية). لسوء الحظ ، فإن عملية الوالدين هذه خارج يدي.

كانت فكرتي الأولى هي الاستيلاء على الوالد الاحتجاج من داخل تطبيق وحدة التحكم ، ومراقبة PID. إذا اختفت عملية الوالدين ، فأنا سوف ينهي تطبيق وحدة التحكم نفسه. حاليا ، أنا أفعل هذا من خلال:

Process process = Process.GetCurrentProcess();
m_ParentPID = 0;
using (ManagementObject mgmtObj = new ManagementObject("win32_process.handle='" +     process.Id.ToString() + "'"))
{
    mgmtObj.Get();
    m_ParentPID = Convert.ToInt32(mgmtObj["ParentProcessId"]);
}
string parentProcessName = Process.GetProcessById(m_ParentPID).ProcessName;
Log("Parent Process: " + parentProcessName + Environment.NewLine);

// Create a timer for monitoring self.
Timer timer = new Timer(new TimerCallback(sender =>
{
    if (m_ParentPID != 0)
    {
        Process parent = System.Diagnostics.Process.GetProcessById(m_ParentPID);
        if (parent == null)
        {
            Log("Parent process stopped/killed.  Terminating self.");
            System.Environment.Exit(0);
        }
    }
}));

// Kick on the timer
timer.Change(m_ExitWatcherFrequency, m_ExitWatcherFrequency);

يعمل هذا جزئيًا فقط - إنه يتوقف عن ارتفاع وحدة المعالجة المركزية ، ولكن إذا نظرت إلى عمليتي من شاشة Sysinternals الرائعة للعملية ، يمكنني رؤية DW20.exe تشغيل - برنامج "Microsoft Application Errating". وهو فقط ... يجلس هناك ، ويظل تطبيق وحدة التحكم في الذاكرة.

ما الذي يجب أن أفعله هنا لإنهاء العملية بشكل صحيح لتجنب ارتفاع وحدة المعالجة المركزية المستمرة والذاكرة التي لم تصدر؟ في النهاية ، يجب أن يكون هذا بدون تدخل.

PS أنا أستخدم تطبيق سطر الأوامر هنا كـ "برنامج طويل المدى" بدلاً من خدمة Windows أو خدمة الويب لأنه لا يمكن تكوين البرنامج الأصل إلا لتنفيذ تطبيق سطر الأوامر الذي يمرر البيانات إليه عبر stdin. (بالنسبة لأولئك الذين يشعرون بالفضول ، هذا هو ejabberd ، باستخدام المصادقة الخارجية).

تعديل:

الرمز الذي ينتظر المدخلات من stdin هو:

// Read data from stdin
char[] charray = new char[maxbuflen];
read = Console.In.Read(charray, 0, 2);

أذكر قبل ذلك عندما ينتهي الوالد ، فإن تطبيق وحدة التحكم يصبح مجنونًا على وحدة المعالجة المركزية. لقد أرفقت مصحح أخطاء من Visual Studio ، وما زلت في الواقع يجلس على Line Console.in.Read. من الناحية النظرية إذن ، عندما يقوم مؤقت المراقبة الذاتي بإعداد ويرى أن الوالد قد اختفى ، فإنه يحاول نظامًا.

هل كانت مفيدة؟

المحلول

يبدو أن عمليتك تدخل في حلقة صلبة بسبب إغلاق دفق إدخال وحدة التحكم عندما يخرج الوالد. هل تتحقق من قيمة الإرجاع من Console.In.Read؟ سيعود الصفر عند إغلاق الدفق. عند هذه النقطة تندلع من الحلقة واتركك Main() طريقة الخروج بنفسها.

وإذا كنت تقوم بتشغيل سلاسل خيوط متعددة ، فسيتعين الانتهاء منها أولاً ، باستخدام Thread.Join أو ما يعادلها.

نصائح أخرى

لمراقبة مخرج العملية ، استخدم معالجة الفصل ، والاشتراك في خرج حدث.

تحرير: إزالة التعليق interop.

تحرير (ردًا على التعليقات):

في معظم الأوقات ، ما يتم القيام به في مثل هذا الموقف ، هو اجتياز بعض البيانات ، بحيث يمكنك التوقف Console.In.Read استدعاء الطريقة.

لذا ، افترض أنك وضعت العلم ، IsDone, ، صحيح عندما تكتشف عملية الوالدين. الآن ، نظرًا لأن العملية التي تنتظرها لن ترسل أي شيء بعد الآن ، فلا تزال بحاجة إلى تلقي شيء ما في الإدخال القياسي ، أو ستحظره إلى الأبد. لذلك ، في رمز معالج الحدث/الموقت ، اكتب شيئًا إلى الإدخال القياسي لعمليتك الخاصة (يمكن أن تكون قيمة خاصة للإشارة إلى أنك قد انتهيت إذا كنت تريد). هذا سوف يجعلك تجاوز Console.In.Read طريقة. بمجرد الخروج ، تحقق لمعرفة ما إذا كان IsDone تم تعيين العلم - إذا كان الأمر كذلك ، توقف عن المعالجة والعودة من طريقتك الرئيسية - لا System.Environment.Exit مطلوب.

سأحل هذا عن طريق إضافة مؤشر ترابط إلى تطبيق وحدة التحكم الخاص بك يراقب أساسًا وجود تطبيق الاتصال في جدول العملية. كل هذا خارج الجزء العلوي من رأسي ، ولكن إليك بعض الرمز الكاذب:

using System.Thread;
using System.Diagnostics;

main(){
     Thread monitoringThread = new Thread(new ThreadStart(monitor));
     monitoringThread.Name = "Monitor";
     monitoringThread.Start();
}

وفي مراقبة الوظيفة:

void monitor(){
     Process[] theCallers = Process.GetProcessesByName("CallingProcessName");
     theCallers[0].WaitForExit();
     Process.GetCurrentProcess().Kill();
}

على افتراض أن لديك عملية استدعاء واحدة فقط في قائمة العمليات ، بمجرد انتهاء هذه العملية ، فإن هذه العملية ستفعل ذلك أيضًا. يمكنك أيضًا وضع رمز التنظيف الجميل لعملية الخاصة بك ، بدلاً من أن ينتهي بشكل مفاجئ.

أحب فكرة MMR عن مشاهدة عملية الوالدين من الطفل. إذا كان لديك وسيلة لتمرير PID الوالد على سطر الأوامر ، فسيكون ذلك أفضل. من داخل العملية الأصل ، يمكنك الحصول على PID:

System.Diagnostics.Process.GetCurrentProcess().Id
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top