Я создаю невидимый компонент в.Net 2.0. Этот компонент использует асинхронный сокет (BeginReceive, EndReceive и т.д.). Асинхронные обратные вызовы называют в контексте рабочего потока, созданного временем выполнения. Пользователю компонента не придется волноваться о многопоточности (Это - главная цель, что я хочу),
Пользователь компонента может создать мой невидимый компонент в любом потоке (поток UI является просто общей нитью для простых приложений. Более серьезные приложения могли создать компонент в произвольном рабочем потоке). Триггерные события компонента, такие как "SessionConnected" или "DataAvailable".
Проблема: из-за Асинхронных Обратных вызовов и событий, сгенерированных там, обработчик событий выполняется в контексте рабочего потока. Я хочу использовать промежуточный слой, которые вынуждают обработчик событий выполниться в контексте потока, который создал компонент в первом месте.
Пример кода (разделенный от обработки исключений и т.д....)
///
/// Occurs when the connection is ended
///
/// The IAsyncResult to read the information from
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();
}
///
/// Occurs when data receive is done; when 0 bytes were received we can assume the connection was closed so we should disconnect
///
/// The IAsyncResult that was used by BeginRead
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();
}
}
Из-за природы Асинхронных сокетов все приложения с помощью моего компонента замусорены "Если (это. InvokeRequired) {..." и все, что я хочу, является пользователем, чтобы смочь использовать мой компонент, без беспокойств в качестве вида вклинивания сигнала.
Таким образом, как я пошел бы о генерировании событий, не требуя, чтобы пользователь проверил InvokeRequired (или, поместите по-другому, как я форсирую события, сгенерированные в том же потоке как поток, который инициировал событие во-первых)?
Я считал материал о AsyncOperation, BackgroundWorkers, SynchronizingObjects, AsyncCallbacks и тоннах другого материала, но все это заставляет мою голову кружиться.
Я действительно придумывал это, конечно, неуклюжее, "решение", но это, кажется, перестало работать в некоторых ситуациях (когда мой компонент называют из проекта WinForms через статический класс, например),
///
/// Raises an event, ensuring BeginInvoke is called for controls that require invoke
///
///
///
/// http://www.eggheadcafe.com/articles/20060727.asp
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. Сообщение, но я не вижу, как применить его к моей ситуации.
Реализация на основе массива полезна, если вам нужна куча, которая используется в качестве приоритетной очереди в алгоритмах графов. В этом случае элементы в куче являются постоянными, при этом появляется самый верхний элемент и вставляются новые элементы. Удаление верхнего элемента (или min-элемента) требует некоторой повторной балансировки, чтобы снова стать кучой, что может быть сделано так, чтобы массив был достаточно сбалансирован.
Эталоном для этого является алгоритм Голдберга и Тарьяна об эффективном вычислении оптимального сетевого потока в направленных графиках, iirc.
-121--3266138- Единственной причиной использования или
является необходимость побитовой арифметики , т.е. необходимо манипулировать битами в числе:
Sub SetBit(value As Integer, Bit As Integer)
value = value Or (1 << Bit)
End Sub
Этот тип является единственным случаем, подходящим для или
. Во всех остальных случаях (т.е. при использовании булевой логики) используйте OrElse
.
Несмотря на схожие названия, Or
и OrElse
являются семантически довольно различными операциями, которые не следует путать друг с другом. Так бывает, что внутреннее представление Boolean
s позволяет использовать побитовые или
для достижения эффекта, сходного (но не такого) с OrElse
. (Старые версии BASIC и VB - до .NET - использовали эту связь только предусматривать операции или
, но не OrElse
.)
Ok; Итак, вот с чем я закончил после еще одного чтения:
public class MyComponent {
private AsyncOperation _asyncOperation;
/// Constructor of my component:
MyComponent() {
_asyncOperation = AsyncOperationManager.CreateOperation(null);
}
/// <summary>
/// Raises an event, ensuring the correct context
/// </summary>
/// <param name="eventDelegate"></param>
/// <param name="args"></param>
protected void RaiseEvent(Delegate eventDelegate, object[] args)
{
if (eventDelegate != null)
{
_asyncOperation.Post(new System.Threading.SendOrPostCallback(
delegate(object argobj)
{
eventDelegate.DynamicInvoke(argobj as object[]);
}), args);
}
}
}
Другое решение, размещенное здесь, было своего рода незавершенным. Решение, размещенное здесь, кажется (согласно MSDN) на данный момент лучшим. Предложения очень, очень приветствуются.
Кажется, я нашёл своё решение:
private SynchronizationContext _currentcontext
/// Constructor of my component:
MyComponent() {
_currentcontext = WindowsFormsSynchronizationContext.Current;
//...or...?
_currentcontext = SynchronizationContext.Current;
}
/// <summary>
/// Raises an event, ensuring the correct context
/// </summary>
/// <param name="eventDelegate"></param>
/// <param name="args"></param>
protected void RaiseEvent(Delegate eventDelegate, object[] args)
{
if (eventDelegate != null)
{
if (_currentcontext != null)
_currentcontext.Post(new System.Threading.SendOrPostCallback(
delegate(object a)
{
eventDelegate.DynamicInvoke(a as object[]);
}), args);
else
eventDelegate.DynamicInvoke(args);
}
}
Я всё ещё тестирую это, но, похоже, оно отлично работает.
Может быть, я не понимаю, но мне кажется, что вы можете просто передать ссылку на пользовательский объект в вашем состоянии Async.
Я собрал следующий пример для иллюстрации;
Сначала у нас есть объект Callback. У него есть 2 свойства - элемент управления, на котором нужно посылать действия, и элемент управления вызовом;
public class Callback
{
public Control Control { get; set; }
public Action Method { get; set; }
}
Затем у меня есть проект WinForms, который вызывает случайный код на другом потоке (используя BeginInvoke), а затем показывает окно сообщений, когда код завершает выполнение.
private void Form1_Load(object sender, EventArgs e)
{
Action<bool> act = (bool myBool) =>
{
Thread.Sleep(5000);
};
act.BeginInvoke(true, new AsyncCallback((IAsyncResult result) =>
{
Callback c = result.AsyncState as Callback;
c.Control.Invoke(c.Method);
}), new Callback()
{
Control = this,
Method = () => { ShowMessageBox(); }
});
}
Метод ShowMessageBox должен работать на потоке пользовательского интерфейса и выглядит следующим образом:
private void ShowMessageBox()
{
MessageBox.Show("Testing");
}
Это то, что вы искали?
.Если ваш компонент всегда должен использоваться одним потоком, вы можете сделать что-нибудь вроде этого:
public delegate void CallbackInvoker(Delegate method, params object[] args);
public YourComponent(CallbackInvoker invoker)
{
m_invoker = invoker;
}
protected void RaiseEvent(Delegate eventDelegate, object[] args)
{
if (eventDelegate != null)
{
try
{
if (m_invoker != null)
m_invoker(eventDelegate, args);
else
eventDelegate.DynamicInvoke(args);
}
catch (Exception ex)
{
Console.WriteLine(ex.GetType());
Console.WriteLine(ex.Message);
//Swallow
}
}
}
Затем, когда вы создаете свой компонент из формы или другого элемента управления, вы можете сделать это:
YourComponent c = new YourComponent(this.Invoke);
Для постановки события в очередь на нерабочем потоке пользовательского интерфейса, он должен иметь какой-нибудь механизм постановки в очередь, затем вы можете дать метод с подписью CallbackInvoker для постановки делегата в очередь на рабочем потоке.