Событие огня от Асинхронного компонента в потоке UI

Я создаю невидимый компонент в.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. Сообщение, но я не вижу, как применить его к моей ситуации.

7
задан Community 23 May 2017 в 12:08
поделиться

4 ответа

Реализация на основе массива полезна, если вам нужна куча, которая используется в качестве приоритетной очереди в алгоритмах графов. В этом случае элементы в куче являются постоянными, при этом появляется самый верхний элемент и вставляются новые элементы. Удаление верхнего элемента (или 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 .)

-121--4223157-

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) на данный момент лучшим. Предложения очень, очень приветствуются.

2
ответ дан 7 December 2019 в 16:42
поделиться

Кажется, я нашёл своё решение:

    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);
        }
    }

Я всё ещё тестирую это, но, похоже, оно отлично работает.

1
ответ дан 7 December 2019 в 16:42
поделиться

Может быть, я не понимаю, но мне кажется, что вы можете просто передать ссылку на пользовательский объект в вашем состоянии 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");
    }

Это то, что вы искали?

.
0
ответ дан 7 December 2019 в 16:42
поделиться

Если ваш компонент всегда должен использоваться одним потоком, вы можете сделать что-нибудь вроде этого:

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 для постановки делегата в очередь на рабочем потоке.

0
ответ дан 7 December 2019 в 16:42
поделиться
Другие вопросы по тегам:

Похожие вопросы: