سؤال

دعونا نقول لدي الدرجة التي تطبق IDisposable واجهة.شيء من هذا القبيل:

http://www.flickr.com/photos/garthof/3149605015/

MyClass يستخدم بعض الموارد غير المدارة, ومن ثم Dispose() طريقة من IDisposable النشرات تلك الموارد. MyClass ينبغي أن تستخدم مثل هذا:

using ( MyClass myClass = new MyClass() ) {
    myClass.DoSomething();
}

الآن, أريد أن تنفيذ أسلوب المكالمات DoSomething() بشكل غير متزامن.إضافة أسلوب جديد MyClass:

http://www.flickr.com/photos/garthof/3149605005/

الآن من جانب العميل ، MyClass ينبغي أن تستخدم مثل هذا:

using ( MyClass myClass = new MyClass() ) {
    myClass.AsyncDoSomething();
}

ومع ذلك ، إذا كنت لا تفعل أي شيء آخر, هذا يمكن أن تفشل مثل الكائن myClass قد يكون التخلص منها قبل DoSomething() ويسمى (ورمي غير متوقع ObjectDisposedException).لذا فإن الدعوة إلى Dispose() طريقة (سواء ضمنية أو صريحة) يجب أن يتأخر حتى استدعاء غير متزامن DoSomething() هو القيام به.

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

شكرا

ملاحظة:من أجل البساطة ، لم دخلت في تفاصيل كيفية التصرف() طريقة تنفيذ.في الحياة الحقيقية وعادة ما تتبع التخلص نمط.


تحديث: شكرا جزيلا على ردودكم.أنا أقدر جهدك.كما chakrit وقد علق, أحتاج أن مكالمات متعددة المتزامن DoSomething يمكن أن تكون مصنوعة.من الناحية المثالية, شيء مثل هذا يجب أن تعمل بشكل جيد:

using ( MyClass myClass = new MyClass() ) {

    myClass.AsyncDoSomething();
    myClass.AsyncDoSomething();

}

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

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

المحلول 6

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

public class MyClass : IDisposable {

    private delegate void AsyncDoSomethingCaller();
    private delegate void AsyncDoDisposeCaller();

    private int pendingTasks = 0;

    public DoSomething() {
        // Do whatever.
    }

    public AsyncDoSomething() {
        pendingTasks++;
        AsyncDoSomethingCaller caller = new AsyncDoSomethingCaller();
        caller.BeginInvoke( new AsyncCallback( EndDoSomethingCallback ), caller);
    }

    public Dispose() {
        AsyncDoDisposeCaller caller = new AsyncDoDisposeCaller();
        caller.BeginInvoke( new AsyncCallback( EndDoDisposeCallback ), caller);
    }

    private DoDispose() {
        WaitForPendingTasks();

        // Finally, dispose whatever managed and unmanaged resources.
    }

    private void WaitForPendingTasks() {
        while ( true ) {
            // Check if there is a pending task.
            if ( pendingTasks == 0 ) {
                return;
            }

            // Allow other threads to execute.
            Thread.Sleep( 0 );
        }
    }

    private void EndDoSomethingCallback( IAsyncResult ar ) {
        AsyncDoSomethingCaller caller = (AsyncDoSomethingCaller) ar.AsyncState;
        caller.EndInvoke( ar );
        pendingTasks--;
    }

    private void EndDoDisposeCallback( IAsyncResult ar ) {
        AsyncDoDisposeCaller caller = (AsyncDoDisposeCaller) ar.AsyncState;
        caller.EndInvoke( ar );
    }
}

قد تحدث بعض المشكلات إذا اثنين أو أكثر من مؤشرات الترابط في محاولة قراءة / كتابة pendingTasks متغير في نفس الوقت ، وبالتالي فإن قفل الكلمة ينبغي أن تستخدم لمنع شروط السباق:

public class MyClass : IDisposable {

    private delegate void AsyncDoSomethingCaller();
    private delegate void AsyncDoDisposeCaller();

    private int pendingTasks = 0;
    private readonly object lockObj = new object();

    public DoSomething() {
        // Do whatever.
    }

    public AsyncDoSomething() {
        lock ( lockObj ) {
            pendingTasks++;
            AsyncDoSomethingCaller caller = new AsyncDoSomethingCaller();
            caller.BeginInvoke( new AsyncCallback( EndDoSomethingCallback ), caller);
        }
    }

    public Dispose() {
        AsyncDoDisposeCaller caller = new AsyncDoDisposeCaller();
        caller.BeginInvoke( new AsyncCallback( EndDoDisposeCallback ), caller);
    }

    private DoDispose() {
        WaitForPendingTasks();

        // Finally, dispose whatever managed and unmanaged resources.
    }

    private void WaitForPendingTasks() {
        while ( true ) {
            // Check if there is a pending task.
            lock ( lockObj ) {
                if ( pendingTasks == 0 ) {
                    return;
                }
            }

            // Allow other threads to execute.
            Thread.Sleep( 0 );
        }
    }

    private void EndDoSomethingCallback( IAsyncResult ar ) {
        lock ( lockObj ) {
            AsyncDoSomethingCaller caller = (AsyncDoSomethingCaller) ar.AsyncState;
            caller.EndInvoke( ar );
            pendingTasks--;
        }
    }

    private void EndDoDisposeCallback( IAsyncResult ar ) {
        AsyncDoDisposeCaller caller = (AsyncDoDisposeCaller) ar.AsyncState;
        caller.EndInvoke( ar );
    }
}

أرى المشكلة مع هذا النهج.والإفراج عن الموارد بشكل غير متزامن فعلت شيئا مثل هذا قد عمل:

MyClass myClass;

using ( myClass = new MyClass() ) {
    myClass.AsyncDoSomething();
}

myClass.DoSomething();

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

نصائح أخرى

يبدو أنك تستخدم القائم على الحدث المتزامن نمط (انظر هنا للحصول على مزيد من المعلومات حول .صافي المتزامن أنماط) إذا ما كنت عادة ما يكون هو الحدث على فئة حرائق عندما المتزامن عملية اكتمال اسمه DoSomethingCompleted (لاحظ أن AsyncDoSomething يجب أن يسمى DoSomethingAsync اتباع نمط بشكل صحيح).مع هذا الحدث يتعرض يمكن أن تكتب:

var myClass = new MyClass();
myClass.DoSomethingCompleted += (sender, e) => myClass.Dispose();
myClass.DoSomethingAsync();

البديل الآخر هو استخدام IAsyncResult نمط ، حيث يمكنك تمرير تفويض تلك المكالمات أسلوب التخلص إلى AsyncCallback المعلمة (مزيد من المعلومات على هذا النمط في الصفحة أعلاه أيضا).في هذه الحالة عليك BeginDoSomething و EndDoSomething أساليب بدلا من DoSomethingAsync, و كان يطلق عليه شيء من هذا القبيل...

var myClass = new MyClass();
myClass.BeginDoSomething(
    asyncResult => {
                       using (myClass)
                       {
                           myClass.EndDoSomething(asyncResult);
                       }
                   },
    null);        

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

المتزامن أساليب وعادة ما يكون رد مما يسمح لك أن تفعل بعض الإجراءات عند completition.إذا كانت هذه هي الحالة سيكون شيئا من هذا القبيل:

// The async method taks an on-completed callback delegate
myClass.AsyncDoSomething(delegate { myClass.Dispose(); });

وسيلة أخرى حول هذا المتزامن المجمع:

ThreadPool.QueueUserWorkItem(delegate
{
    using(myClass)
    {
        // The class doesn't know about async operations, a helper method does that
        myClass.DoSomething();
    }
});

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

هل يمكن إضافة آلية الاستدعاء و تمرير تنظيف وظيفة رد الاتصال.

var x = new MyClass();

Action cleanup = () => x.Dispose();

x.DoSomethingAsync(/*and then*/cleanup);

ولكن هذا من شأنه أن يشكل مشكلة إذا كنت ترغب في تشغيل عدة المتزامن يدعو من نفس الكائن سبيل المثال.

طريقة واحدة وسيتم تطبيق بسيط عد سيمافور مع إشارة الدرجة لحساب عدد من التشغيل المتزامن وظائف.

إضافة العداد MyClass و على كل AsyncWhatever المكالمات الcounter على مخارج decerement ذلك.عندما سيمافور هو 0 ، ثم فئة هو على استعداد ليتم التخلص منها.

var x = new MyClass();

x.DoSomethingAsync();
x.DoSomethingAsync2();

while (x.RunningJobsCount > 0)
    Thread.CurrentThread.Sleep(500);

x.Dispose();

ولكن أشك في أن يكون الطريقة المثلى.أنا رائحة تصميم المشكلة.ربما إعادة التفكير في MyClass التصاميم يمكن تجنب هذا ؟

يمكنك مشاركة بعض الشيء من MyClass التنفيذ ؟ ماذا من المفترض أن أفعل ؟

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

أفضل رهان هو على الارجح أن يكون كل الأشياء التي تهم تم إنشاؤها ضمن القاسم المشترك (ربما UI) ، في محاولة ضمان أن الموضوع سوف يبقى حول مدى الحياة من الكائنات الفائدة و استخدام شيء من هذا القبيل Control.BeginInvoke طلب الكائنات' التخلص منها.شريطة أن لا إنشاء كائن ولا تنظيف كتلة لأي فترة من الزمن, قد يكون نهجا جيدا ، ولكن إذا كان أي عملية يمكن أن كتلة مقاربة مختلفة قد تكون هناك حاجة [ربما تفتح خفية الدمية شكل مع موضوع خاص بها ، لذلك يمكن للمرء أن استخدام Control.BeginInvoke هناك].

بدلا من ذلك, إذا كان لديك السيطرة على IDisposable تطبيقات التصميم عليها بحيث يمكن أن يكون بأمان أطلقت بشكل غير متزامن.في كثير من الحالات التي من شأنها أن "مجرد عمل" بشرط أن لا أحد يحاول استخدام هذا البند عندما يتم التخلص منها ، ولكن هذا لا يكاد معين.ولا سيما مع العديد من أنواع IDisposable, هناك خطر حقيقي من أن عدة مثيلات الكائن قد كل من التلاعب مشتركة خارج الموارد [مثلكائن قد عقد List<> من الحالات التي تم إنشاؤها ، إضافة مثيلات تلك القائمة عندما يتم بناؤها ، وإزالة الحالات على Dispose;إذا كانت قائمة عمليات غير متزامنة, غير متزامن Dispose يمكن الفاسدة القائمة حتى لو الكائن الذي يتم التخلص منها هو لا خلاف في الاستخدام.

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

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