كيف يمكنني ضمان atomicity من عملية الحصول على و-مجموعة لإعادة توجيه Console.Out للإخراج وحدة تسجيل؟

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

سؤال

وأحتاج لاعتراض تيار حدة الانتاج (ق) من أجل الاستيلاء عليها لسجل لكن لا تزال تمر الأمور من خلال لتيار الأصلي بحيث يعمل التطبيق بشكل صحيح. وهذا يعني بوضوح تخزين TextWriter Console.Out الأصلي قبل تغييره مع Console.SetOut(new MyTextWriterClass(originalOut)).

وأفترض العمليات الفردية للحصول على الملكية خارج والدعوة تنفذ طريقة SetOut () من خلال Console بطريقة موضوع آمن. ولكن أود أن نتأكد من أن بعض الصفحات الأخرى (على سبيل المثال. تشغيل التعليمات البرمجية تطبيق العميل الذي لا سيطرة ولا يمكن أن نتوقع أن تتغير، ولذا لا يمكن الاعتماد على بلدي خطة تأمين مخصصة) لا يمكن قصد تغييره في بلدي بين الحصول على وتعيين وينتهي الأمر الحصول على الكتابة بواسطة أغير عليه (كسر السلوك تطبيقها المفضل). منذ تعليمات برمجية أخرى قد ببساطة تدعو SetOut ()، أن قانون بلدي مثالي الحصول على نفس القفل استخدامها داخليا من قبل Console (على افتراض وجود واحد).

ومما يؤسف له، Console هو (ثابتة) فئة، لا مثيل، لذلك لا يمكنك فقط lock (Console). وتبحث في وثائق الطبقة لا يبدو أن هناك أي ذكر للتأمين. ليس هذا هو الاستخدام المتوقع عادة من الأساليب وحدة التحكم هذه، ولكن يجب أن تكون هناك طريقة آمنة للقيام بذلك باعتبارها عملية الذرية.

وإذا تعذر نظام تأمين القياسية، وهناك طريقة أخرى لضمان ذلك؟ لمثل هذا مقطع حرج القصير (وذلك مرة واحدة فقط)، وحتى للحظات حظر كافة المواضيع الأخرى قد يكون مقبولا، إذا كان هذا هو السبيل الوحيد للقيام بذلك. نحن نستخدم C # و .NET2.0.

إذا لم يكن حتى أن من الممكن (دون تعطيل تطبيق العميل)، ثم سيتعين علينا أن نعتمد فقط على كونها من المستبعد جدا أن تطبيق العميل أن إعادة توجيه الانتاج جهازها <ط> و يحدث للقيام بذلك في بين لنا الحصول على وتعيين العمليات. أود فقط لتغطية جميع القواعد، فقط في حالة.

تعديل : في الآن أن لدينا إجابة محددة مع رمز المثال، لقد إعادة صياغة عنوان المسألة إلى أكثر عموما تعكس حالات الاستخدام حيث الجواب (ق) يمكن أن تساعد، لتكون أكثر وضوحا . أيضا، أضاف علامة ل "الذري".

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

المحلول

إذا نظرتم الى التنفيذ لSetOut يبدو موضوع آمن لي:

[HostProtection(SecurityAction.LinkDemand, UI=true)]
public static void SetOut(TextWriter newOut)
{
    if (newOut == null)
    {
        throw new ArgumentNullException("newOut");
    }
    new SecurityPermission(SecurityPermissionFlag.UnmanagedCode).Demand();
    _wasOutRedirected = true;
    newOut = TextWriter.Synchronized(newOut);
    lock (InternalSyncObject)
    {
        _out = newOut;
    }
}

تحرير

ووcloest شيء إلى حل أتمكن من الخروج مع تستخدم انعكاس للحصول على InternalSyncObject وثم قفل على ذلك.

وهناك كلمة تحذير، وهذا هو فكرة سيئة متردده وينبغي أن تستخدم فقط عند وجود أي خيار آخر. هل يمكن أن تتسبب في إطار تتصرف unexpectadly وتعطل عملية.

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

وحظا سعيدا: -)

نصائح أخرى

حسنا، الآن لقد كان بعض الوقت للعمل فعليا لتنفيذ النهج انعكاس اقترح جوش. رمز الأساسي (في صفي ConsoleIntercepter، الذي يرث من TextWriter) هو:

private static object GetConsoleLockObject()
{
    object lockObject;
    try
    {
        const BindingFlags bindingFlags = BindingFlags.GetProperty |
            BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public;
        // It's currently private, but we'd be happy if it were public, too.
        Type consoleType = typeof(Console);

        lockObject = consoleType.InvokeMember("InternalSyncObject", bindingFlags,
                                              null, null, null);
    }
    catch
    {
        lockObject = null; // Return null on any failure.
    }
    return lockObject;
}
public static void RegisterConsoleIntercepter()
{
    object lockObject = GetConsoleLockObject();
    if (lockObject != null)
    {
        // Great!  We can make sure any other changes happen before we read
        // or after we've written, making this an atomic replacement operation.
        lock (lockObject)
        {
            DoIntercepterRegistration();
        }
    }
    else
    {
        // Couldn't get the lock object, but we still need to work, so
        // just do it without an outer lock, and keep your fingers crossed.
        DoIntercepterRegistration();
    }
}

وثم DoIntercepterRegistration() سيكون شيئا مثل:

private static void DoIntercepterRegistration()
{
    Console.SetOut(new ConsoleIntercepter(Console.Out));
    Console.SetError(new ConsoleIntercepter(Console.Error));
}

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

لاحظ أن (في التعليمات البرمجية المصدر المنصوص عليها .NET2.0) يستخدم فئة وحدة التحكم InternalSyncObject لحماية تهيئة خارج وخطأ وحماية SetOut () وSetError ()، ولكنه لا يستخدم قفل جميع أنحاء يقرأ من من والخطأ بمجرد أن يتم تهيئة سابقا. وهذا يكفي لحالتي خاصة، ولكن يمكن أن يكون انتهاكات atomicitiy إذا كنت فعلت شيئا أكثر تعقيدا (ومجنون)؛ لا أستطيع التفكير في أي سيناريوهات مفيدة مع هذه المشكلة، ولكن يمكن أن يتصور تلك المرضية بشكل متعمد.

إذا كنت تستطيع القيام بذلك في وقت مبكر من Main() سيكون لديك فرصة أفضل لتجنب أي شروط السباق، خاصة إذا كنت تستطيع ان تفعل ذلك قبل شيء يخلق ترابط عامل. أو في المنشئ ثابت من بعض الطبقة.

إذا وضعت Main() مع [STAThread] يعزو ذلك سيقوم بتشغيل في شقة مترابطة واحدة بحيث ينبغي أن تساعد أيضا.

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

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top