Нужен способ, чтобы внешний интерфейс ожидал завершения длинного процесса, не останавливая поток системных сообщений - PullRequest
0 голосов
/ 07 февраля 2012

Я работаю с C # и .NET 4.0.

Моя программа состоит из внешнего интерфейса и внутреннего интерфейса. Серверная часть будет работать как служба Windows, которая запускается автоматически при запуске системы. Внешний интерфейс - это приложение WPF, которое пользователь запустит, когда будет готово к работе с приложением.

В рамках процесса запуска клиенту необходимо установить связь с серверной частью. Это делается с помощью XMPP и работает нормально. Код, выполняющий связь, имеет метод BeginStart, который возвращает WaitHandle. Внутри try-catch я вызываю BeginStart, получаю WaitHandle и вызываю метод WaitOne (TimeSpan) Waithandle. Обратите внимание, что в этот момент программа отображает заставку. И эти вызовы выполняются во внешнем потоке.

Хотя это работает нормально, я обнаружил, что нажатие на другие окна во время вызова WaitOne не имеет никакого эффекта. По сути, весь трафик сообщений Windows останавливается в ожидании возврата этого WaitHandle.

Я считаю, что мне нужно выполнить вызовы BeginStart и WaitOne в другом потоке; Мне все еще нужно, чтобы интерфейс дождался возвращения WaitOne, так как программа должна была умереть, и пользовательский интерфейс не должен отображаться, пока вызов WaitOne не завершится успешно (без тайм-аута). Просто нужно позволить другим программам обрабатывать свои сообщения Windows.

Как мне сделать эту работу? В WPF нет метода Application.DoEvents.

Tony

Edit:

Похоже, будет полезно, если я предоставлю еще немного информации.

Мое приложение WPF является пользовательским интерфейсом для этого продукта и составляет только половину всего продукта. Другая половина работает как служба Windows на том же ПК, и мы называем это back-end. То, что происходит в бэкэнде, неважно, за исключением того, что оно прослушивает соединения с ним с помощью XMPP.

Когда пользовательский интерфейс (или интерфейс) запускается, одна из вещей, которую он должен сделать, это установить связь с сервером с помощью XMPP. Обычно это занимает максимум пару секунд. Тем не менее, может быть что-то не так или сервер может быть недоступен, когда пользователь запускает интерфейс. Я не хочу, чтобы интерфейс ждал бесконечно; через разумный промежуток времени он должен истечь, вывести сообщение об ошибке и умереть.

Чтобы установить связь, есть модуль, который запускается как отдельный поток, который я запускаю. Я вызываю его метод BeginStart, который возвращает WaitHandle. Затем я подожду WaitHandle, передав интервал TimeSpan, установленный для моего периода ожидания. Вот фрагмент кода:

try {
    WaitHandle handle = BackgroundProcess.BeginStart();
    if ( !handle.WaitOne( Timeout ) ) {
        // Something went wrong; Notify user here
        return false;
    }

    // Make sure that the Background Process has started all of it's internal modules
    bool allAreRunning;
    Stopwatch timer = new Stopwatch();
    timer.Reset(); timer.Start();
    do {
        allAreRunning = true;
        foreach ( Module module in BackgroundProcess.Modules ) {
            if ( module.State != Module.States.Running ) {
                allAreRunning = false;
                break;
            }
        }

        if ( !allAreRunning ) {
            // I hate this loop, but I can't think of a better way to do this
            Thread.Sleep( 1000 );
        }

    } while ( !allAreRunning && timer.ElapsedMilliseconds <= Timeout.TotalMilliseconds );

    timer.Stop();

    if ( !allAreRunning ) {
        // Something went wrong; Notify user here
        return false;
    }

    // Other initialization done here

} catch ( Exception ex ) {
    // Something went wrong; Notify user here
    return false;
}

Чтобы проверить это, я запустил программу в отладчике, когда серверная часть не работала. Я могу нажать на любое открытое окно, переключиться на него и нормально с ним взаимодействовать. Что я не могу сделать, это нажать на рабочем столе. Вызов WaitHandle.WaitOne () возвращается очень быстро, поскольку он возвращается только после запуска фонового процесса и не ожидает установления соединения. Это цикл do-while, который выполняется большую часть времени ожидания.

Мне нужно переписать это, чтобы пользователь мог переключаться не только на открытие программ, но и на рабочий стол. Я просто не знаю, как заставить это работать.

Tony

Ответы [ 2 ]

1 голос
/ 07 февраля 2012

Вы можете переместить ваш вызов в BackgroundWorker и перехватить событие WorkerCompleted по окончании загрузки.Вы также можете обновить прогресс пользовательского интерфейса с помощью события ProgressChanged.

0 голосов
/ 16 февраля 2012

Я нашел лучший способ кодирования этой логики запуска.Код, однако, опирается на архитектуру нашего внутреннего кода.

Мне удалось удалить цикл do-while в фрагменте кода и заменить его более качественным кодом.Теперь код зацикливается на всех объектах Module и запрашивает их для их состояния выполнения WaitHandle.Они добавляются в коллекцию списка.После завершения цикла я преобразую список в массив, вызывая .ToArray (), а затем вызываю метод WaitHandle.WaitAll.

Теперь код выглядит примерно так:

try {
    WaitHandle handle = BackgroundProcess.BeginStart();
    if ( !handle.WaitOne( Timeout ) ) {
        . . .
       return false;
    }

    List<WaitHandle> waitHandles = new List<WaitHandle>();
    foreach ( Module module in BackgroundProcess.Transit.SubModules ) {
        WaitHandle waitHandle = module.GetWaitHandleForState( Module.States.Running );
        waitHandles.Add( waitHandle );
    }

    if ( !WaitHandle.WaitAll( waitHandles.ToArray(), Timeout ) ) {
        . . . 
        return false;
    }

    // If we get here, everything is communicating properly.
    . . .

} catch ( Exception ex ) {
    . . .
    return false;
}

return true;

Спасибо

Тони

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...