Я создаю невизуальный компонент в .Net 2.0. Этот компонент использует асинхронный сокет (BeginReceive, EndReceive и т. Д.). Асинхронные обратные вызовы вызываются в контексте рабочего потока, созданного средой выполнения. Пользователю компонента не нужно беспокоиться о многопоточности (это основная цель, чего я хочу)
Пользователь компонента может создать мой невизуальный компонент в любом потоке (поток пользовательского интерфейса - это просто общий поток для простых приложений. Более серьезные приложения могут создавать компонент в произвольном рабочем потоке). Компонент запускает такие события, как «SessionConnected» или «DataAvailable».
Проблема: из-за асинхронных обратных вызовов и событий, вызванных в них, обработчик событий выполняется в контексте рабочего потока. Я хочу использовать промежуточный уровень, который заставляет обработчик событий выполняться в контексте потока, который в первую очередь создал компонент.
Пример кода (без обработки исключений и т. Д.)
/// <summary>
/// Occurs when the connection is ended
/// </summary>
/// <param name="ar">The IAsyncResult to read the information from</param>
private void EndConnect(IAsyncResult ar)
{
// pass connection status with event
this.Socket.EndConnect(ar);
this.Stream = new NetworkStream(this.Socket);
// -- FIRE CONNECTED EVENT HERE --
// Setup Receive Callback
this.Receive();
}
/// <summary>
/// Occurs when data receive is done; when 0 bytes were received we can assume the connection was closed so we should disconnect
/// </summary>
/// <param name="ar">The IAsyncResult that was used by BeginRead</param>
private void EndReceive(IAsyncResult ar)
{
int nBytes;
nBytes = this.Stream.EndRead(ar);
if (nBytes > 0)
{
// -- FIRE RECEIVED DATA EVENT HERE --
// Setup next Receive Callback
if (this.Connected)
this.Receive();
}
else
{
this.Disconnect();
}
}
Из-за природы сокетов Async все приложения, использующие мой компонент, завалены «If (this.InvokeRequired) {...», и все, что я хочу, это чтобы пользователь мог без проблем использовать мой компонент как своего рода каплю. -в.
Итак, как мне поднять события, не требуя от пользователя проверки InvokeRequired (или, иначе говоря, как мне принудительно инициировать события, возникающие в том же потоке, что и поток, который инициировал событие в первую очередь)?
Я читал про AsyncOperation, BackgroundWorkers, SynchronizingObjects, AsyncCallbacks и множество других вещей, но все это заставляет меня кружиться голова.
Я придумал это, конечно, неуклюжее «решение», но в некоторых ситуациях оно, похоже, не работает (например, когда мой компонент вызывается из проекта WinForms через статический класс)
/// <summary>
/// Raises an event, ensuring BeginInvoke is called for controls that require invoke
/// </summary>
/// <param name="eventDelegate"></param>
/// <param name="args"></param>
/// <remarks>http://www.eggheadcafe.com/articles/20060727.asp</remarks>
protected void RaiseEvent(Delegate eventDelegate, object[] args)
{
if (eventDelegate != null)
{
try
{
Control ed = eventDelegate.Target as Control;
if ((ed != null) && (ed.InvokeRequired))
ed.Invoke(eventDelegate, args);
else
eventDelegate.DynamicInvoke(args);
}
catch (Exception ex)
{
Console.WriteLine(ex.GetType());
Console.WriteLine(ex.Message);
//Swallow
}
}
}
Любая помощь будет оценена. Заранее спасибо!
РЕДАКТИРОВАТЬ: согласно этот поток, лучше всего было бы использовать SyncrhonizationContext.Post но я не понимаю, как применить это к моей ситуации.