سؤال

معرفتي متعددة الخيوط لا تزال بدائية جدا، لذلك سوف نقدر حقا بعض المؤشرات هنا. لدي واجهة، IOPERALINVOKER (من WCF) والتي لديها الطرق التالية:

IAsyncResult InvokeBegin(object instance, object[] inputs, AsyncCallback callback, object state)
object InvokeEnd(object instance, out object[] outputs, IAsyncResult result)

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

في الوقت الحالي، أنا أفعل شيئا مثل هذا:

public StaOperationSyncInvoker : IOperationInvoker {
   IOperationInvoker _innerInvoker;
   public StaOperationSyncInvoker(IOperationInvoker invoker) {
       this._innerInvoker = invoker;
   } 


    public IAsyncResult InvokeBegin(object instance, object[] inputs, AsyncCallback callback, object state)
    {
        Thread t = new Thread(BeginInvokeDelegate);
        InvokeDelegateArgs ida = new InvokeDelegateArgs(_innerInvoker, instance, inputs, callback, state);
        t.SetApartmentState(ApartmentState.STA);
        t.Start(ida);
        // would do t.Join() if doing syncronously
        // how to wait to get IAsyncResult?
        return ida.AsyncResult;
    }

    public object InvokeEnd(object instance, out object[] outputs, IAsyncResult result)
    {
        // how to call invoke end on the 
        // thread? could we have wrapped IAsyncResult
        // to get a reference here?
        return null;
    }

    private class InvokeDelegateArgs {
        public InvokeDelegateArgs(IOperationInvoker invoker, object instance, object[] inputs, AsyncCallback callback, object state)
        {
            this.Invoker = invoker;
            this.Instance = instance;
            this.Inputs = inputs;
            this.Callback = callback;
            this.State = state;
        }

        public IOperationInvoker Invoker { get; private set; }
        public object Instance { get; private set; }
        public AsyncCallback Callback { get; private set; }
        public IAsyncResult AsyncResult { get; set; }
        public Object[] Inputs { get; private set; }
        public Object State { get; private set; }
    }
    private static void BeginInvokeDelegate(object data)
    {
        InvokeDelegateArgs ida = (InvokeDelegateArgs)data;
        ida.AsyncResult = ida.Invoker.InvokeBegin(ida.Instance, ida.Inputs, ida.Callback, ida.State);
    }
}

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

تشكرات،

جوامع

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

المحلول

أسهل طريقة لتنفيذ طريقة متزامنة غير متزامنة هي وضعها في مندوب، واستخدام BeginInvoke و EndInvoke أساليب المندوب الناتج. سيؤدي ذلك إلى تشغيل الطريقة المتزامنة على موضوع ThreadPool، و BeginInvoke سوف يعود IAsyncResult التنفيذ، لذلك ليس لديك لتنفيذ الشجاعة منه. ومع ذلك، تحتاج إلى تهريب بعض البيانات الإضافية قليلا في IAsyncResult عاد من قبل IOperationInvoker.InvokeEnd. وبعد يمكنك القيام بذلك بسهولة عن طريق إنشاء تنفيذ IAsyncResult أن يفوض كل شيء إلى الداخلية IAsyncResult, ، ولكن لديه حقل إضافي لاحتواء المندوب، بحيث IAsyncResult يتم تمرير المثيل إلى InvokeEnd, ، يمكنك الوصول إلى المندوب للاتصال EndInvoke عليه.

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

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

إليك بسيطة للغاية ولكنها غير فعالة بشكل رهيب، تنفيذ IAsyncResult. وبعد إنه يغلف جميع الميزات الأساسية: حجج تمرير، حدث المزامنة، تنفيذ رد الاتصال، استثناءات الانتشار من مهمة ASYNC وعودة النتيجة.

using System;
using System.Threading;

class MyAsyncResult : IAsyncResult
{
    object _state;
    object _lock = new object();
    ManualResetEvent _doneEvent = new ManualResetEvent(false);
    AsyncCallback _callback;
    Exception _ex;
    bool _done;
    int _result;
    int _x;

    public MyAsyncResult(int x, AsyncCallback callback, object state)
    {
        _callback = callback;
        _state = state;
        _x = x; // arbitrary argument(s)
    }

    public int X { get { return _x; } }

    public void SignalDone(int result)
    {
        lock (_lock)
        {
            _result = result;
            _done = true;
            _doneEvent.Set();
        }
        // never invoke any delegate while holding a lock
        if (_callback != null)
            _callback(this); 
    }

    public void SignalException(Exception ex)
    {
        lock (_lock)
        {
            _ex = ex;
            _done = true;
            _doneEvent.Set();
        }
        if (_callback != null)
            _callback(this);
    }

    public object AsyncState
    {
        get { return _state; }
    }

    public WaitHandle AsyncWaitHandle
    {
        get { return _doneEvent; }
    }

    public bool CompletedSynchronously
    {
        get { return false; }
    }

    public int Result
    {
        // lock (or volatile, complex to explain) needed
        // for memory model problems.
        get
        {
            lock (_lock)
            {
                if (_ex != null)
                    throw _ex;
                return _result;
            }
        }
    }

    public bool IsCompleted
    {
        get { lock (_lock) return _done; }
    }
}

class Program
{
    static void MyTask(object param)
    {
        MyAsyncResult ar = (MyAsyncResult) param;
        try
        {
            int x = ar.X;
            Thread.Sleep(1000); // simulate lengthy work
            ar.SignalDone(x * 2); // demo work = double X
        }
        catch (Exception ex)
        {
            ar.SignalException(ex);
        }
    }

    static IAsyncResult Begin(int x, AsyncCallback callback, object state)
    {
        Thread th = new Thread(MyTask);
        MyAsyncResult ar = new MyAsyncResult(x, callback, state);
        th.Start(ar);
        return ar;
    }

    static int End(IAsyncResult ar)
    {
        MyAsyncResult mar = (MyAsyncResult) ar;
        mar.AsyncWaitHandle.WaitOne();
        return mar.Result; // will throw exception if one 
                           // occurred in background task
    }

    static void Main(string[] args)
    {
        // demo calling code
        // we don't need state or callback for demo
        IAsyncResult ar = Begin(42, null, null); 
        int result = End(ar);
        Console.WriteLine(result);
        Console.ReadLine();
    }
}

من المهم التصحيح أن رمز العميل لا يمكن أن يرى IAsyncResult التنفيذ، وإلا فإنها قد تصل إلى طرق مثل SignalException بشكل غير لائق أو قراءة Result قبل الأوان. يمكن أن تكون الفئة أكثر كفاءة من خلال عدم بناء WaitHandle تطبيق (ManualResetEvent في المثال) إذا لم يكن ذلك ضروريا، ولكن هذا صعب يحصل 100٪ على حق. أيضا، Thread و ManualResetEvent يمكن وينبغي التخلص منها في End التنفيذ، كما يجب القيام به مع جميع الكائنات التي تنفذ IDisposable. وبعد ومن الواضح، End يجب أن تحقق للتأكد من تنفيذ الفئة المناسبة للحصول على استثناء أجمل من استثناء يلقي. لقد تركت هذه التفاصيل وغيرها من أنها تحجب الميكانيكا الأساسية لتنفيذ ASYNC.

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