Вызвать метод в главном потоке без элемента управления WinForm, чтобы вызвать Invoke или BeginInvoke - PullRequest
5 голосов
/ 16 августа 2010

Я хочу запустить операцию в фоновом потоке.Когда он закончится, я хочу проверить наличие ошибок и перебросить их в исходную ветку.

Я использую фоновый работник.Создание исключения в обработчике событий RunWorkerCompleted приводит к необработанному исключению - это имеет смысл, если обработчик событий выполняется в фоновом потоке.Если бы у меня был элемент управления winform, я мог бы вызвать Invoke или BeginInvoke, но у меня нет элемента управления winform в этом объекте, хотя это проект winform.

Как я могу повторно вызвать исключение, которое произошло в фоновом режиме?

private void bgw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
    {
        if (e.Error != null)
        {              
            // I want to throw an exception here, without causing an unhandled exception and without being able to call Invoke or BeginInvoke on a WinForm control. 
        }
        else if (e.Cancelled)
        {
           // Do something useful
        }
        else
        {
            if (e.Result != null)
            {
               // Do something with the result
            }
        }
    }

Я бы предположил, что обработчик событий RunWorkerCompleted будет работать в исходном вызывающем потоке.Возможно, в этом случае мне не нужен фоновый работник.

Ответы [ 6 ]

7 голосов
/ 16 августа 2010

Невозможно внедрить код в другой работающий поток. Даже операционная система не может сделать это.

Control.BeginInvoke работает, помещая ссылку на делегат в очередь, а затем используя PostMessage для отправки пользовательского сообщения в очередь сообщений потока пользовательского интерфейса. Цикл сообщений Application.Run ищет это сообщение, а когда обнаруживает, извлекает делегата из очереди и выполняет его.

Суть в том, что нет другого способа сделать то, что вам нужно, без кодирования вашего основного потока для поиска какого-либо сигнала (или сообщения) из другого потока.

Добавлена ​​

Вы заявили, что это приложение WinForm, но у вас нет элемента управления для использования BeginInvoke.

Редактировать: я предложил ленивый груз, не продумывая его. Элемент управления может в конечном итоге быть создан в неправильном потоке .

Предварительно создайте элемент управления до Application.Run, который будет существовать в течение всего срока службы приложения. Вы можете использовать это для BeginInvoke от.

Редактировать # 3

Итак, я пытаюсь убедиться, что это работает, и, конечно, это не так. Вы не можете просто создать универсальный элемент управления, он должен иметь дескриптор HWND. Простое исправление: создайте его так:

invokerControl = new Control();
invokerControl.CreateControl();

Это позволит вам BeginInvoke из него, даже если нет открытых объектов Form для вызова из.

1 голос
/ 16 августа 2010

Если вы не создали экземпляр BGW в потоке пользовательского интерфейса, его событие RunWorkerCompleted будет выполняться в произвольном потоке потоков.Любое исключение, которое вы выбрасываете в этом потоке, не может быть перехвачено и завершит ваше приложение последним вздохом через AppDomain.UnhandledException.

В этом случае BGW больше не используется.Приятно убедиться, что его события выполняются в потоке пользовательского интерфейса.Вы также можете использовать MethodInvoker.BeginInvoke ().Вам нужно будет немного обдумать это и решить, что именно вы будете делать, когда часть кода в каком-то рабочем потоке не справляется со своей задачей.Устранить такой сбой, как правило, невозможно, и позволить программному обеспечению аварийно завершить работу программы.

Если вы действительно хотите каким-то способом уведомить пользователя и попытаться заставить программу спотыкаться, тогдавам действительно следует создать экземпляр BGW в потоке пользовательского интерфейса.И используйте, скажем, MessageBox.Show () в обработчике событий RunWorkerCompleted.Когда вы это сделаете, обязательно восстановите состояние вашей программы, вам почти наверняка понадобится условие catch в DoWork () для очистки шрапнели.

1 голос
/ 16 августа 2010

Вы можете проверить с другой стороны.Я имею в виду - поместите таймер (который будет работать в том же главном потоке, что и форма) в вашей форме, и один раз в секунду - проверьте некоторое поле Исключения в вашей форме (с помощью lock ()), а также поле некоторого объекта, чтобы определить, что операция завершена.,А затем из bgw_RunWorkerCompleted обернуть код с помощью try ... catch и при catch (снова с помощью lock ()) установить поле Exception формы для перехваченного исключения.Но почему бы не использовать Invoke или BeginInvoke?

0 голосов
/ 16 августа 2010

Если случайно вы используете 4.0, вы можете переключиться на использование Задачи, которая будет делать то, что вы хотите. Посмотрите этот пример

0 голосов
/ 16 августа 2010

Просто обработайте событие RunWorkerCompleted. Это событие синхронизировано для вас

    BackgroundWorker bgw;

    private void button1_Click(object sender, EventArgs e)
    {
        bgw = new BackgroundWorker();
        bgw.DoWork +=new DoWorkEventHandler(bgw_DoWork);
        bgw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bgw_RunWorkerCompleted);
        bgw.RunWorkerAsync(bgw);
    }

    void bgw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
    {
        if (e.Error != null)
            textBox1.Text = e.Error.Message;
    }

    void bgw_DoWork(object sender, DoWorkEventArgs e)
    {
        throw new NotImplementedException();
    }
0 голосов
/ 16 августа 2010

Не выбрасывайте исключение.

Создайте событие в фоновом режиме, на которое подписан ваш основной поток приложения, а затем обработайте ошибку там, выбрасывая исключение при необходимости.

...