سؤال

لدي تطبيق ويب الذي يتحكم في تطبيقات الويب التي يتم المرور من موازن التحميل.تطبيق ويب يعمل على كل فرد الخادم.

ومن يتتبع من "أو" دولة لكل تطبيق في كائن في ASP.NET تطبيق الدولة ، والهدف من ذلك هو تسلسل إلى ملف على القرص عندما يتم تغيير حالة.الدولة هو إلغاء تسلسل من الملف عند تطبيق ويب يبدأ.

في حين أن الموقع نفسه فقط يحصل على بضعة طلبات الثانية قمم و الملف فإنه نادرا ما يتم الوصول إليها, لقد وجدت أنه من السهل للغاية لسبب للحصول على الاصطدامات أثناء محاولة القراءة من أو الكتابة إلى الملف.هذه الآلية يجب أن تكون موثوق بها للغاية لأن لدينا نظام آلي أن يفعل بانتظام المتداول نشر الخادم.

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

وقال أن التعليمات البرمجية التي تستخدم للتحكم في الوصول إلى الملف يبدو مثل هذا:

internal static Mutex _lock = null;
/// <summary>Executes the specified <see cref="Func{FileStream, Object}" /> delegate on 
/// the filesystem copy of the <see cref="ServerState" />.
/// The work done on the file is wrapped in a lock statement to ensure there are no 
/// locking collisions caused by attempting to save and load the file simultaneously 
/// from separate requests.
/// </summary>
/// <param name="action">The logic to be executed on the 
/// <see cref="ServerState" /> file.</param>
/// <returns>An object containing any result data returned by <param name="func" />. 
///</returns>
private static Boolean InvokeOnFile(Func<FileStream, Object> func, out Object result)
{
    var l = new Logger();
    if (ServerState._lock.WaitOne(1500, false))
    {
        l.LogInformation( "Got lock to read/write file-based server state."
                        , (Int32)VipEvent.GotStateLock);
        var fileStream = File.Open( ServerState.PATH, FileMode.OpenOrCreate 
                                  , FileAccess.ReadWrite, FileShare.None);                
        result = func.Invoke(fileStream);                
        fileStream.Close();
        fileStream.Dispose();
        fileStream = null;
        ServerState._lock.ReleaseMutex();
        l.LogInformation( "Released state file lock."
                        , (Int32)VipEvent.ReleasedStateLock);
        return true;
    }
    else
    {
        l.LogWarning( "Could not get a lock to access the file-based server state."
                    , (Int32)VipEvent.CouldNotGetStateLock);
        result = null;
        return false;
    }
}

هذا عادة يعمل ولكن أحيانا لا أستطيع الوصول إلى مزامنة (أنا راجع "لا يمكن الحصول على تأمين" الحدث في السجل).أنا لا يمكن استخراج هذا محليا يحدث فقط على خوادم الإنتاج (Win Server 2k3/IIS 6).إذا كنت إزالة مهلة التطبيق معلقة إلى أجل غير مسمى (حالة السباق??), بما في ذلك الطلبات اللاحقة.

عندما كنت لا تحصل على الأخطاء ، تبحث في سجل الأحداث يقول لي أن مزامنة قفل كان تحقيق الصادرة عن الطلب السابق قبل كان خطأ تسجيل.

مزامنة هو مثيل في Application_Start الحدث.أحصل على نفس النتائج عندما يتم إنشاء مثيل ثابت في الإعلان.

كلها أعذار:خيوط/تأمين ليس لي موطن, وأنا عموما لا داعي للقلق حول هذا الموضوع.

أي اقتراحات لماذا عشوائيا سوف تفشل في الحصول على إشارة ؟


تحديث:

لقد تم إضافة السليم معالجة الخطأ (كيف محرج!), ولكن أنا لا يزال الحصول على نفس الأخطاء - و للعلم الاستثناءات غير المعالجة لم تكن أبدا مشكلة.

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

@mmr:كيف يتم استخدام رصد أي مختلفة من استخدام مزامنة?استنادا إلى وثائق MSDN, يبدو أنه فعال تفعل نفس الشيء - إذا كنت لا تستطيع الحصول على قفل مع مزامنة ، لا فشل بأمان بمجرد العودة كاذبة.

شيء آخر هو أن نلاحظ:القضايا أواجه يبدو أن عشوائي تماما - إذا فشل في طلب واحد ، قد تعمل بشكل جيد في اليوم التالي.هناك لا يبدو أن يكون نمط ، إما (بالتأكيد لا كل ، على الأقل).


تحديث 2:

هذا القفل لا يستخدم أي مكالمة أخرى.المرة الوحيدة _lock المشار إليها خارج InvokeOnFile الطريقة عندما يتم إنشاء مثيل.

ظائفها أن يتم استدعاء إما القراءة من الملف و تسلسل إلى كائن أو التسلسلية كائن الكتابة إلى الملف.لا يتم إجراء العملية في موضوع مستقل.

ServerState.المسار هو ثابت للقراءة فقط الميدانية التي لا أتوقع أن يسبب أي مشاكل التزامن.

أود أيضا أن إعادة تكرار بلدي نقطة في وقت سابق أنه لا يمكن إنتاج هذه محليا (في كاسيني).


الدروس المستفادة:

  • الاستخدام السليم معالجة الخطأ (طبعا!)
  • استخدام الأداة المناسبة لهذا المنصب (و يكون فهم أساسي من ما/كيف أنه أداة لا).كما سامبو ، باستخدام مزامنة على ما يبدو الكثير من النفقات العامة ، الذي كان يسبب مشاكل في التطبيق ، في حين رصد مصممة خصيصا .صافي.
هل كانت مفيدة؟

المحلول

يجب أن يكون فقط باستخدام Mutexes إذا كنت بحاجة إلى عبر عملية المزامنة.

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

إذا كنت بحاجة إلى الدعم بين عملية تأمين كنت في حاجة الى العالمية مزامنة.

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

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

أعتقد أن الأفضل أن إعادة كتابة هذا استخدام القفل.أيضا, يبدو أنك تدعو إلى أسلوب آخر ، إذا كان هذا يستغرق إلى الأبد, قفل سيعقد إلى الأبد.هذا خطر جدا.

هذا هو على حد سواء أقصر و أكثر أمانا:

// if you want timeout support use 
// try{var success=Monitor.TryEnter(m_syncObj, 2000);}
// finally{Monitor.Exit(m_syncObj)}
lock(m_syncObj)
{
    l.LogInformation( "Got lock to read/write file-based server state."
                    , (Int32)VipEvent.GotStateLock);
    using (var fileStream = File.Open( ServerState.PATH, FileMode.OpenOrCreate
                                     , FileAccess.ReadWrite, FileShare.None))
    {
        // the line below is risky, what will happen if the call to invoke
        // never returns? 
        result = func.Invoke(fileStream);
    }
}

l.LogInformation("Released state file lock.", (Int32)VipEvent.ReleasedStateLock);
return true;

// note exceptions may leak out of this method. either handle them here.
// or in the calling method. 
// For example the file access may fail of func.Invoke may fail

نصائح أخرى

إذا كان بعض من عمليات الملف تفشل القفل لن يتم الافراج عنهم.على الأرجح هذا هو الحال.ضع الملف في عمليات حاول/catch و الافراج عن قفل في النهاية كتلة.

على أي حال, إذا كنت تقرأ في ملف العالمية.asax Application_Start الأسلوب ، وهذا يضمن أن لا أحد آخر هو العامل على ذلك (قلت أن الملف للقراءة على بدء التطبيق ، أليس كذلك؟).لتجنب التصادم في تجمع التطبيقات restaring ، وما إلى ذلك ، يمكنك فقط محاولة لقراءة الملف (على افتراض أن عملية الكتابة يأخذ حصري قفل) ، ومن ثم الانتظار من 1 ثانية و إعادة المحاولة إذا طرح استثناء.

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

أرى عددا من القضايا المحتملة هنا.

تحرير تحديث 2:إذا كانت وظيفة بسيطة تسلسل/إلغاء تسلسل الجمع ، كنت منفصلة اثنين إلى اثنين من وظائف مختلفة ، واحدة في 'تسلسل' وظيفة واحدة إلى 'إلغاء تسلسل' وظيفة.هم حقا اثنين من المهام المختلفة.ثم هل يمكن أن يكون مختلف, قفل مهام محددة.الاحتجاج هو أنيق, ولكن لقد حصلت في الكثير من المتاعب نفسي ذاهب 'أنيق' أكثر من 'العمل'.

1) هو LogInformation وظيفة قفل ؟ لأن تسميه داخل مزامنة أولا ثم بمجرد الافراج عن مزامنة.حتى إذا كان هناك قفل الكتابة إلى ملف سجل/هيكل, ثم يمكنك في نهاية المطاف مع حالة السباق هناك.لتجنب ذلك ، وضع سجل داخل القفل.

2) تحقق من استخدام رصد الدرجة التي أعرف يعمل في C# و افترض يعمل في ASP.NET.لذلك يمكنك ببساطة مجرد محاولة للحصول على قفل و تفشل برشاقة على خلاف ذلك.طريقة واحدة لاستخدام هذا هو فقط أحاول الحصول على القفل.(تحرير لماذا:انظر هنا;أساسا ، مزامنة عبر عمليات رصد في عملية واحدة ، ولكن تم تصميمه .صافي و حتى هو المفضل.وجود تفسير تعطى من قبل الأطباء.)

3) ماذا يحدث إذا filestream افتتاح فشل, لأن شخصا آخر قد القفل ؟ التي من شأنها أن رمي استثناء ، و التي يمكن أن تسبب هذا الرمز إلى التصرف بشكل سيئ (أي قفل لا يزال محتجزا من قبل مؤشر ترابط التي استثناء, و موضوع آخر يمكن أن تحصل في ذلك).

4) ماذا عن وظائفها نفسها ؟ لا أن تبدأ موضوع آخر ، أو هو تماما داخل مؤشر ترابط واحد ؟ ماذا عن الوصول إلى ServerState.الطريق ؟

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

    Object MyLock = new Object();
    private static Boolean InvokeOnFile(Func<FileStream, Object> func, out Object result)
{
    var l = null;
    var filestream = null;
    Boolean success = false;
    if (Monitor.TryEnter(MyLock, 1500))
        try {
            l = new Logger();
            l.LogInformation("Got lock to read/write file-based server state.", (Int32)VipEvent.GotStateLock);
            using (fileStream = File.Open(ServerState.PATH, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.None)){                
                result = func.Invoke(fileStream); 
            }    //'using' means avoiding the dispose/close requirements
            success = true;
         }
         catch {//your filestream access failed

            l.LogInformation("File access failed.", (Int32)VipEvent.ReleasedStateLock);
         } finally {
            l.LogInformation("About to released state file lock.", (Int32)VipEvent.ReleasedStateLock);
            Monitor.Exit(MyLock);//gets you out of the lock you've got
        }
    } else {
         result = null;
         //l.LogWarning("Could not get a lock to access the file-based server state.", (Int32)VipEvent.CouldNotGetStateLock);//if the lock doesn't show in the log, then it wasn't gotten; again, if your logger is locking, then you could have some issues here
    }
  return Success;
}
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top