// в OnChange выставлять флаг что нужно обновлять SharedData - далее в одновном цикле анализировать флаг private void Dependency_OnChange(object sender, OracleNotificationEventArgs e) { StringBuilder Tables = new StringBuilder(); foreach (var Name in e.ResourceNames) { if (Name.ToUpper().Contains(NotifierCheckTable)) { NotifierCheckState = NotifierCheckStates.CheckSuccess; } else { Tables.AppendFormat("{0} ", Name); Interlocked.Increment(ref Notifications); } } if (Notifications > 0) { EventHandle.Set(); // wake up load thread Log.Message("Уведомление об изменениях: " + Tables); } }
private void LoadThreadExecute() { ChangeStatus("Started"); try { while (!LoadThreadTerminated) { ShowFullStatus("Sleeping..."); EventHandle.WaitOne(); // спим до получения уведомления о событии ShowFullStatus("Loading new generation..."); try { if (Notifications > 0) Log.Message("Обнаружены изменения в БД..."); Notifications = 0; // обнуляем счётчик уведомлений перед рестартом, т.к. уведомление может прийти и после рестарта. Атомарная операция if (NotifierCheckState == NotifierCheckStates.CheckFailed) // после этого всё равно загружаем новые данные - вдруг мы что-то пропустили, пока система уведомлений не работала { Log.Message("Требуется перезапуск подсистемы слежения за изменениями БД!"); try { RestartNotifier(); // перезапускаем нотификатор, если он не прошел проверку, после чего устанавливаем состояние Idle NotifierCheckState = NotifierCheckStates.WasRestarted; } catch (Exception e) { if (e is ThreadAbortException || e is ThreadInterruptedException) throw; // для выхода в родительский try... Log.Message("Ошибка в цикле ожидания при перезапуске подсистемы отслеживания: {0}", e.Message); } } Load(); // после перезапуска нотификатора начинаем заново грузить ОРД } catch (Exception e) { if (e is ThreadAbortException || e is ThreadInterruptedException) return; else Log.Message("Ошибка цикла ожидания момента обновления ОРД: {0}\n{1}", e.Message, e.StackTrace); } } } catch (Exception e) { if (e is ThreadAbortException || e is ThreadInterruptedException) return; else Log.Message("Ошибка потока загрузки ОРД: {0}\n{1}", e.Message, e.StackTrace); } }
private void CheckNotifier() { try { NotifierCheckState = NotifierCheckStates.Idle; while (true) { //Log.Message("Check Notifier state: {0}", NotifierCheckState); switch (NotifierCheckState) { case NotifierCheckStates.Idle: var StartWaitTime = DateTime.Now; ShowFullStatus("Sleeping..."); while (DateTime.Now - StartWaitTime <= CheckChangeNotifierInterval) { Thread.Sleep(1000); if (NotifierCheckState != NotifierCheckStates.Idle) break; } if (NotifierCheckState != NotifierCheckStates.CheckSuccess) NotifierCheckState = NotifierCheckStates.StartCheck; break; case NotifierCheckStates.StartCheck: using (var c = new OracleConnection(ConnectionString)) { c.AutoCommit = false; c.Open(); NotifierCheckState = NotifierCheckStates.CheckStarted; // must be set before, because notification can come right away NotifierCheckStartTime = DateTime.Now; int RowsUpdated = OraQuery.ExecuteNonQuery(c, string.Format("update {0} set id = id + 1 where rownum = 1", NotifierCheckTable)); if (RowsUpdated > 0) c.Commit(); else c.Rollback(); } break; case NotifierCheckStates.CheckStarted: // started, in processes if (DateTime.Now - NotifierCheckStartTime >= CheckChangeNotifierTimeout) // if no answer after starting NotifierCheckState = NotifierCheckStates.CheckFailed; else Thread.Sleep(1000); break; case NotifierCheckStates.CheckSuccess: case NotifierCheckStates.WasRestarted: NotifierCheckState = NotifierCheckStates.Idle; break; case NotifierCheckStates.CheckFailed: // need restart Log.Message("Check Notifier state: {0}", NotifierCheckState); // It is important state EventHandle.Set(); // wake up thread for restart Thread.Sleep(1000); break; default: Log.Message("Unknown status: {0}", NotifierCheckState); break; } } } catch (Exception e) { if (e is ThreadAbortException || e is ThreadInterruptedException) return; else Log.Message("Ошибка потока проверки нотификатора: {0}\n{1}", e.Message, e.StackTrace); } }