WP7 - Не удается корректно выйти из потока bg при деактивации или закрытии приложения - PullRequest
3 голосов
/ 20 октября 2011

Мое приложение wp7 с большим объемом данных сохраняет данные следующим образом: я веду журнал изменений, отражающий все действия пользователя, и каждые несколько секунд таймер потока запускает поток пула потоков, который сбрасывает журнал изменений в базу данных внутри транзакции. , Это выглядит примерно так:

Когда пользователь выходит из системы, я останавливаю таймер, очищаю журнал в потоке пользовательского интерфейса (занимает не более секунды или двух) и отключаю БД.

Однако, если рабочий поток активен , когда пользователь выходит, я не могу понять, как изящно реагировать. Кажется, что система уничтожает рабочий поток, поэтому она никогда не завершает свою работу и никогда не снимает свою блокировку на соединении с базой данных, а поток пользовательского интерфейса затем пытается получить блокировку и немедленно уничтожается системой. Я попытался установить флаг в потоке пользовательского интерфейса, запрашивая отмену работника, но я думаю, что рабочий был прерван, прежде чем он прочитал флаг. Все работает отлично, за исключением этого сценария «1 на 100», когда некоторые пользовательские изменения не сохраняются в БД, и я не могу обойти это.

Очень упрощенный код ниже:

private Timer _SweepTimer = new Timer(SweepCallback, null, 5000, 5000);

private volatile bool _BailOut = false;
private void SweepCallback(object state) {
    lock (db) { 
        db.startTransaction();

        foreach(var entry in changeJournal){
            //CRUD entry as appropriate
            if(_BailOut){
                db.rollbackTransaction();
                return;
            }
        }

        db.endTransaction();
        changeJournal.Clear();
    }
}

private void RespondToSystemExit(){
    _BailOut = true; //Set flag for worker to exit

    lock(db){ //In theory, should acquire the lock after the bg thread bails out
        SweepCallback(null);//Flush to db on the UI thread
        db.dismount();//App is now ready to close
    }
}

Ответы [ 3 ]

1 голос
/ 25 октября 2011

Ну, просто для того, чтобы закрыть этот вопрос, я закончил тем, что использовал блокировку вручную, а не блокировку, что, насколько я понимаю, является неправильным использованием ручного восстановления, рискованным и взломанным, но лучше, чем ничего.1002 * Я до сих пор не знаю, почему мой оригинальный код не работал.

РЕДАКТИРОВАТЬ: Для потомков я публикую код, чтобы воспроизвести это с форумов MS:* Вот почти идентичный код, который не работает для WP7, просто создайте страницу с двумя кнопками и подключите их

using System;
using System.Diagnostics;
using System.Threading;
using System.Windows;
using Microsoft.Phone.Controls;

namespace WorkerThreadDemo {
    public partial class MainPage : PhoneApplicationPage {
        public MainPage() {
            InitializeComponent();
        }

        private Object lockObj = new Object();
        private volatile bool WorkerCancel = false;
        private void buttonStartWorker_Click(object sender, RoutedEventArgs e) {
            ThreadPool.QueueUserWorkItem((obj) => {
                if (Monitor.TryEnter(lockObj)) {
                    try {
                        Log("Worker acquired the lock");
                        for (int x = 0; x < 10; x++) {
                            Thread.Sleep(1200);
                            Log("Worker: tick" + x.ToString());
                            if (WorkerCancel) {
                                Log("Worker received exit signal, exiting");
                                WorkerCancel = false;
                                break;
                            }
                        }
                    } finally {
                        Monitor.Exit(lockObj);
                        Log("Worker released the lock");
                    }
                } else {
                    Log("Worker failed to acquire lock");
                }
            });
        }

        private void Log(string Data) {
            Debug.WriteLine(string.Format("{0} - {1}", DateTime.Now.ToString("mm:ss:ffff"), Data));
        }

        private void buttonInterrupt_Click(object sender, RoutedEventArgs e) {
            Log("UI thread - Setting interrupt flag");
            WorkerCancel = true;

            //Thread.Sleep(3000); UNCOMMENT ME AND THIS WILL START TO WORK! 
            if (Monitor.TryEnter(lockObj, 5000)) {
                try {
                    Log("UI thread - successfully acquired lock from worker");
                } finally {
                    Monitor.Exit(lockObj);
                    Log("UI thread - Released the lock");
                }
            } else {
                Log("UI thread - failed to acquire the lock from the worker");
            }
        }
    }
}
0 голосов
/ 26 октября 2011

Как уже сказал Генрих Ульбрихт, у вас есть <= 10 секунд, чтобы закончить свои вещи, но вы должны заблокировать MainThread, чтобы получить их. </p>

Это означает, что даже если у вас BG-поток с большой работой, но ваш UI-поток просто ничего не делает в OnClosingEvent / OnDeactivatingEvent - вы не получите свои 10 секунд.

Наше приложение фактически выполняет вечное ожидание в потоке пользовательского интерфейса при закрытии, чтобы позволить потоку BG передавать некоторые данные через сокеты.

0 голосов
/ 21 октября 2011

Ваш подход должен работать, когда вы работаете с событием Application_Deactivated или Application_Closing. MSDN сообщает :

Существует ограничение по времени для завершения события Деактивировано.Устройство может завершить приложение, если для сохранения переходного состояния требуется более 10 секунд.

Так что, если вы говорите, что это займет всего несколько секунд, это будет хорошо.Если только документы не расскажут всю историю.Или ваш рабочий поток занимает больше времени, чем вы думаете.

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