العمليات غير المتزامنة داخل عملية غير متزامنة
-
11-09-2019 - |
سؤال
معرفتي متعددة الخيوط لا تزال بدائية جدا، لذلك سوف نقدر حقا بعض المؤشرات هنا. لدي واجهة، 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.